1use crate::classical_ml_integration::{HybridPipelineManager, PipelineConfig};
7use crate::domain_templates::{Domain, DomainTemplateManager, TemplateConfig};
8use crate::error::{MLError, Result};
9use crate::keras_api::{
10 ActivationFunction, Dense, LossFunction, MetricType, OptimizerType, QuantumAnsatzType,
11 QuantumDense, Sequential,
12};
13use crate::model_zoo::{ModelZoo, QuantumModel};
14use crate::optimization::{OptimizationMethod, Optimizer};
15use crate::pytorch_api::{
16 ActivationType as PyTorchActivationType, QuantumLinear, QuantumModule, QuantumSequential,
17};
18use crate::qnn::{QNNBuilder, QuantumNeuralNetwork};
19use crate::qsvm::{FeatureMapType, QSVMParams, QSVM};
20use crate::variational::{VariationalAlgorithm, VariationalCircuit};
21use quantrs2_circuit::prelude::*;
22use quantrs2_core::prelude::*;
23use scirs2_core::ndarray::{Array1, Array2, ArrayD, Axis};
24use serde::{Deserialize, Serialize};
25use std::collections::HashMap;
26
27pub struct TutorialManager {
29 tutorials: HashMap<TutorialCategory, Vec<Tutorial>>,
31 exercises: HashMap<String, Exercise>,
33 progress: HashMap<String, TutorialProgress>,
35}
36
37#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
39pub enum TutorialCategory {
40 Fundamentals,
42 QuantumNeuralNetworks,
44 Algorithms,
46 Variational,
48 Optimization,
50 Hybrid,
52 Applications,
54 Advanced,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct Tutorial {
61 pub id: String,
63 pub title: String,
65 pub description: String,
67 pub category: TutorialCategory,
69 pub difficulty: DifficultyLevel,
71 pub prerequisites: Vec<String>,
73 pub learning_objectives: Vec<String>,
75 pub duration_minutes: usize,
77 pub sections: Vec<TutorialSection>,
79 pub exercises: Vec<String>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub enum DifficultyLevel {
86 Beginner,
88 Intermediate,
90 Advanced,
92 Expert,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct TutorialSection {
99 pub title: String,
101 pub content: String,
103 pub code_examples: Vec<CodeExample>,
105 pub interactive_elements: Vec<InteractiveElement>,
107 pub key_concepts: Vec<String>,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct CodeExample {
114 pub title: String,
116 pub description: String,
118 pub code: String,
120 pub expected_output: Option<String>,
122 pub explanation: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct InteractiveElement {
129 pub element_type: InteractiveType,
131 pub title: String,
133 pub instructions: String,
135 pub parameters: HashMap<String, String>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub enum InteractiveType {
142 Visualization,
144 ParameterTuning,
146 CodeCompletion,
148 Quiz,
150 Experiment,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Exercise {
157 pub id: String,
159 pub title: String,
161 pub description: String,
163 pub exercise_type: ExerciseType,
165 pub instructions: Vec<String>,
167 pub starter_code: Option<String>,
169 pub solution_code: String,
171 pub test_cases: Vec<TestCase>,
173 pub hints: Vec<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub enum ExerciseType {
180 Coding,
182 CircuitDesign,
184 ParameterOptimization,
186 AlgorithmImplementation,
188 DataAnalysis,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct TestCase {
195 pub description: String,
197 pub input: String,
199 pub expected_output: String,
201 pub points: usize,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct TutorialProgress {
208 pub user_id: String,
210 pub completed_tutorials: Vec<String>,
212 pub completed_exercises: Vec<String>,
214 pub current_tutorial: Option<String>,
216 pub scores: HashMap<String, f64>,
218 pub time_spent: HashMap<String, usize>,
220}
221
222impl TutorialManager {
223 pub fn new() -> Self {
225 let mut manager = Self {
226 tutorials: HashMap::new(),
227 exercises: HashMap::new(),
228 progress: HashMap::new(),
229 };
230 manager.register_tutorials();
231 manager.register_exercises();
232 manager
233 }
234
235 fn register_tutorials(&mut self) {
237 self.register_fundamentals_tutorials();
238 self.register_qnn_tutorials();
239 self.register_algorithm_tutorials();
240 self.register_variational_tutorials();
241 self.register_optimization_tutorials();
242 self.register_hybrid_tutorials();
243 self.register_application_tutorials();
244 self.register_advanced_tutorials();
245 }
246
247 fn register_fundamentals_tutorials(&mut self) {
249 let mut tutorials = Vec::new();
250
251 tutorials.push(Tutorial {
253 id: "qc_intro".to_string(),
254 title: "Introduction to Quantum Computing".to_string(),
255 description: "Learn the fundamental concepts of quantum computing: qubits, superposition, entanglement, and quantum gates.".to_string(),
256 category: TutorialCategory::Fundamentals,
257 difficulty: DifficultyLevel::Beginner,
258 prerequisites: vec!["Basic linear algebra".to_string()],
259 learning_objectives: vec![
260 "Understand what qubits are and how they differ from classical bits".to_string(),
261 "Learn about superposition and quantum state representation".to_string(),
262 "Understand entanglement and its role in quantum computing".to_string(),
263 "Familiarize with basic quantum gates and circuits".to_string(),
264 ],
265 duration_minutes: 45,
266 sections: vec![
267 TutorialSection {
268 title: "What are Qubits?".to_string(),
269 content: "A qubit is the fundamental unit of quantum information. Unlike classical bits that can only be 0 or 1, qubits can exist in a superposition of both states simultaneously.".to_string(),
270 code_examples: vec![
271 CodeExample {
272 title: "Creating a Qubit".to_string(),
273 description: "Create a simple qubit in QuantRS2".to_string(),
274 code: r#"
275use quantrs2_core::prelude::*;
276
277// Create a qubit in |0⟩ state
278let qubit = QubitState::new(Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0));
279
280// Create a qubit in superposition |+⟩ = (|0⟩ + |1⟩)/√2
281let superposition = QubitState::new(
282 Complex64::new(1.0/2.0_f64.sqrt(), 0.0),
283 Complex64::new(1.0/2.0_f64.sqrt(), 0.0)
284);
285"#.to_string(),
286 expected_output: Some("Qubit states created successfully".to_string()),
287 explanation: "This example shows how to create qubits in different states using QuantRS2.".to_string(),
288 }
289 ],
290 interactive_elements: vec![
291 InteractiveElement {
292 element_type: InteractiveType::Visualization,
293 title: "Bloch Sphere Visualization".to_string(),
294 instructions: "Visualize different qubit states on the Bloch sphere".to_string(),
295 parameters: HashMap::new(),
296 }
297 ],
298 key_concepts: vec![
299 "Qubits can be in superposition".to_string(),
300 "Quantum states are represented by complex amplitudes".to_string(),
301 "Measurement collapses the superposition".to_string(),
302 ],
303 },
304 TutorialSection {
305 title: "Quantum Gates".to_string(),
306 content: "Quantum gates are the building blocks of quantum circuits. They perform unitary operations on qubits.".to_string(),
307 code_examples: vec![
308 CodeExample {
309 title: "Basic Quantum Gates".to_string(),
310 description: "Apply basic quantum gates using QuantRS2".to_string(),
311 code: r#"
312use quantrs2_circuit::prelude::*;
313
314// Create a quantum circuit
315let mut circuit = QuantumCircuit::new(2);
316
317// Apply Hadamard gate to create superposition
318circuit.h(0);
319
320// Apply CNOT gate to create entanglement
321circuit.cnot(0, 1);
322
323// Apply Pauli-X gate (bit flip)
324circuit.x(1);
325
326// Apply Pauli-Z gate (phase flip)
327circuit.z(0);
328"#.to_string(),
329 expected_output: Some("Circuit with basic gates created".to_string()),
330 explanation: "This demonstrates the most common quantum gates and how to use them in circuits.".to_string(),
331 }
332 ],
333 interactive_elements: vec![
334 InteractiveElement {
335 element_type: InteractiveType::Experiment,
336 title: "Gate Effect Explorer".to_string(),
337 instructions: "Experiment with different gates and see their effect on qubit states".to_string(),
338 parameters: HashMap::new(),
339 }
340 ],
341 key_concepts: vec![
342 "Quantum gates are unitary operations".to_string(),
343 "Hadamard gate creates superposition".to_string(),
344 "CNOT gate creates entanglement".to_string(),
345 "Pauli gates perform rotations".to_string(),
346 ],
347 },
348 ],
349 exercises: vec!["qc_basic_gates".to_string(), "qc_bell_state".to_string()],
350 });
351
352 tutorials.push(Tutorial {
354 id: "qc_circuits".to_string(),
355 title: "Quantum Circuits and Measurement".to_string(),
356 description: "Learn how to construct quantum circuits and understand quantum measurement.".to_string(),
357 category: TutorialCategory::Fundamentals,
358 difficulty: DifficultyLevel::Beginner,
359 prerequisites: vec!["qc_intro".to_string()],
360 learning_objectives: vec![
361 "Build quantum circuits with multiple qubits".to_string(),
362 "Understand quantum measurement and Born rule".to_string(),
363 "Learn about quantum circuit simulation".to_string(),
364 "Implement basic quantum algorithms".to_string(),
365 ],
366 duration_minutes: 60,
367 sections: vec![
368 TutorialSection {
369 title: "Building Quantum Circuits".to_string(),
370 content: "Quantum circuits are composed of quantum gates applied to qubits in a specific sequence.".to_string(),
371 code_examples: vec![
372 CodeExample {
373 title: "Multi-Qubit Circuit".to_string(),
374 description: "Create a circuit with multiple qubits and gates".to_string(),
375 code: r#"
376use quantrs2_circuit::prelude::*;
377
378// Create a 3-qubit circuit
379let mut circuit = QuantumCircuit::new(3);
380
381// Create GHZ state: (|000⟩ + |111⟩)/√2
382circuit.h(0); // Put first qubit in superposition
383circuit.cnot(0, 1); // Entangle first and second qubits
384circuit.cnot(1, 2); // Entangle second and third qubits
385
386// Add measurement
387circuit.measure_all();
388"#.to_string(),
389 expected_output: Some("GHZ state circuit created".to_string()),
390 explanation: "This creates a maximally entangled 3-qubit state called the GHZ state.".to_string(),
391 }
392 ],
393 interactive_elements: vec![],
394 key_concepts: vec![
395 "Quantum circuits process quantum information".to_string(),
396 "Gates are applied sequentially".to_string(),
397 "Multi-qubit entanglement is possible".to_string(),
398 ],
399 },
400 ],
401 exercises: vec!["qc_ghz_state".to_string()],
402 });
403
404 self.tutorials
405 .insert(TutorialCategory::Fundamentals, tutorials);
406 }
407
408 fn register_qnn_tutorials(&mut self) {
410 let mut tutorials = Vec::new();
411
412 tutorials.push(Tutorial {
414 id: "qnn_intro".to_string(),
415 title: "Introduction to Quantum Neural Networks".to_string(),
416 description: "Learn the basics of quantum neural networks and how they differ from classical neural networks.".to_string(),
417 category: TutorialCategory::QuantumNeuralNetworks,
418 difficulty: DifficultyLevel::Intermediate,
419 prerequisites: vec!["qc_circuits".to_string(), "Basic neural networks".to_string()],
420 learning_objectives: vec![
421 "Understand quantum neural network architecture".to_string(),
422 "Learn about parameterized quantum circuits".to_string(),
423 "Implement a simple QNN for classification".to_string(),
424 "Compare QNN vs classical NN performance".to_string(),
425 ],
426 duration_minutes: 90,
427 sections: vec![
428 TutorialSection {
429 title: "QNN Architecture".to_string(),
430 content: "Quantum Neural Networks use parameterized quantum circuits to process and learn from data.".to_string(),
431 code_examples: vec![
432 CodeExample {
433 title: "Simple QNN".to_string(),
434 description: "Create a basic quantum neural network".to_string(),
435 code: r#"
436use quantrs2_ml::prelude::*;
437
438// Create QNN builder
439let mut qnn_builder = QNNBuilder::new(4) // 4 qubits
440 .add_layer(QNNLayer::Embedding { rotation_gates: vec!["RY", "RZ"] })
441 .add_layer(QNNLayer::Entangling { entangling_gate: "CNOT" })
442 .add_layer(QNNLayer::Parameterized {
443 gates: vec!["RY", "RZ"],
444 num_parameters: 8
445 });
446
447// Build the QNN
448let mut qnn = qnn_builder.build()?;
449
450// Train on sample data
451let X = Array2::random((100, 4), Uniform::new(-1.0, 1.0)?);
452let y = Array1::from_vec(vec![0.0; 50].into_iter().chain(vec![1.0; 50]).collect());
453
454qnn.train(&X.into_dyn(), &y.into_dyn().insert_axis(Axis(1)))?;
455"#.to_string(),
456 expected_output: Some("QNN trained successfully".to_string()),
457 explanation: "This creates a parameterized quantum circuit that can learn from classical data.".to_string(),
458 }
459 ],
460 interactive_elements: vec![
461 InteractiveElement {
462 element_type: InteractiveType::ParameterTuning,
463 title: "QNN Hyperparameter Tuning".to_string(),
464 instructions: "Adjust QNN parameters and observe training performance".to_string(),
465 parameters: HashMap::new(),
466 }
467 ],
468 key_concepts: vec![
469 "QNNs use parameterized quantum circuits".to_string(),
470 "Data encoding is crucial for QNN performance".to_string(),
471 "Entangling layers create quantum correlations".to_string(),
472 ],
473 },
474 ],
475 exercises: vec!["qnn_classification".to_string()],
476 });
477
478 self.tutorials
479 .insert(TutorialCategory::QuantumNeuralNetworks, tutorials);
480 }
481
482 fn register_algorithm_tutorials(&mut self) {
484 let mut tutorials = Vec::new();
485
486 tutorials.push(Tutorial {
488 id: "qsvm_tutorial".to_string(),
489 title: "Quantum Support Vector Machines".to_string(),
490 description: "Learn how to implement and use Quantum Support Vector Machines for classification tasks.".to_string(),
491 category: TutorialCategory::Algorithms,
492 difficulty: DifficultyLevel::Intermediate,
493 prerequisites: vec!["qnn_intro".to_string(), "Classical SVM knowledge".to_string()],
494 learning_objectives: vec![
495 "Understand quantum kernel methods".to_string(),
496 "Implement QSVM for binary classification".to_string(),
497 "Compare quantum vs classical kernels".to_string(),
498 "Optimize QSVM hyperparameters".to_string(),
499 ],
500 duration_minutes: 75,
501 sections: vec![
502 TutorialSection {
503 title: "Quantum Kernels".to_string(),
504 content: "Quantum kernels map classical data to quantum feature spaces where linear separation may be easier.".to_string(),
505 code_examples: vec![
506 CodeExample {
507 title: "QSVM Implementation".to_string(),
508 description: "Create and train a Quantum SVM".to_string(),
509 code: r#"
510use quantrs2_ml::prelude::*;
511
512// Create QSVM with ZZ feature map
513let qsvm_params = QSVMParams {
514 feature_map: FeatureMapType::ZZFeatureMap,
515 num_qubits: 4,
516 depth: 2,
517 entanglement: "linear".to_string(),
518 alpha: 1.0,
519};
520
521let mut qsvm = QSVM::new(qsvm_params)?;
522
523// Generate sample data
524let X = Array2::random((100, 4), Uniform::new(-1.0, 1.0)?);
525let y = Array1::from_vec((0..100).map(|i| if i < 50 { -1.0 } else { 1.0 }).collect());
526
527// Train the QSVM
528qsvm.fit(&X.into_dyn(), &y.into_dyn())?;
529
530// Make predictions
531let predictions = qsvm.predict(&X.into_dyn())?;
532"#.to_string(),
533 expected_output: Some("QSVM trained and predictions made".to_string()),
534 explanation: "This demonstrates training a QSVM with quantum kernels for classification.".to_string(),
535 }
536 ],
537 interactive_elements: vec![],
538 key_concepts: vec![
539 "Quantum kernels exploit quantum feature spaces".to_string(),
540 "Feature maps encode classical data into quantum states".to_string(),
541 "Quantum advantage possible in high-dimensional spaces".to_string(),
542 ],
543 },
544 ],
545 exercises: vec!["qsvm_iris".to_string()],
546 });
547
548 self.tutorials
549 .insert(TutorialCategory::Algorithms, tutorials);
550 }
551
552 fn register_variational_tutorials(&mut self) {
554 let mut tutorials = Vec::new();
555
556 tutorials.push(Tutorial {
558 id: "vqe_tutorial".to_string(),
559 title: "Variational Quantum Eigensolver (VQE)".to_string(),
560 description: "Learn to implement VQE for finding ground state energies of quantum systems.".to_string(),
561 category: TutorialCategory::Variational,
562 difficulty: DifficultyLevel::Advanced,
563 prerequisites: vec!["qnn_intro".to_string(), "Quantum chemistry basics".to_string()],
564 learning_objectives: vec![
565 "Understand the VQE algorithm".to_string(),
566 "Implement VQE for small molecules".to_string(),
567 "Learn about ansatz design".to_string(),
568 "Optimize VQE parameters".to_string(),
569 ],
570 duration_minutes: 120,
571 sections: vec![
572 TutorialSection {
573 title: "VQE Algorithm".to_string(),
574 content: "VQE combines quantum circuits with classical optimization to find ground state energies.".to_string(),
575 code_examples: vec![
576 CodeExample {
577 title: "VQE for H2 Molecule".to_string(),
578 description: "Implement VQE to find H2 ground state energy".to_string(),
579 code: r#"
580use quantrs2_ml::prelude::*;
581
582// Create VQE for H2 molecule
583let mut vqe = VariationalAlgorithm::new(2) // 2 qubits for H2
584 .with_ansatz("UCCSD") // Unitary Coupled Cluster ansatz
585 .with_optimizer(OptimizationMethod::LBFGS)
586 .build()?;
587
588// Define H2 Hamiltonian (simplified)
589let hamiltonian = Array2::from_shape_vec(
590 (4, 4),
591 vec![
592 -1.05, 0.0, 0.0, 0.0,
593 0.0, -0.4, -0.2, 0.0,
594 0.0, -0.2, -0.4, 0.0,
595 0.0, 0.0, 0.0, -1.05,
596 ]
597)?;
598
599// Run VQE optimization
600let result = vqe.minimize(&hamiltonian)?;
601println!("Ground state energy: {:.6}", result.energy);
602"#.to_string(),
603 expected_output: Some("Ground state energy: -1.857275".to_string()),
604 explanation: "This implements VQE to find the ground state energy of a hydrogen molecule.".to_string(),
605 }
606 ],
607 interactive_elements: vec![
608 InteractiveElement {
609 element_type: InteractiveType::Visualization,
610 title: "VQE Convergence Plot".to_string(),
611 instructions: "Visualize how VQE energy converges during optimization".to_string(),
612 parameters: HashMap::new(),
613 }
614 ],
615 key_concepts: vec![
616 "VQE finds ground states variationally".to_string(),
617 "Ansatz choice affects performance".to_string(),
618 "Classical optimizer minimizes expectation value".to_string(),
619 ],
620 },
621 ],
622 exercises: vec!["vqe_lih".to_string()],
623 });
624
625 self.tutorials
626 .insert(TutorialCategory::Variational, tutorials);
627 }
628
629 fn register_optimization_tutorials(&mut self) {
631 let mut tutorials = Vec::new();
632
633 tutorials.push(Tutorial {
635 id: "qaoa_tutorial".to_string(),
636 title: "Quantum Approximate Optimization Algorithm (QAOA)".to_string(),
637 description: "Learn QAOA for solving combinatorial optimization problems.".to_string(),
638 category: TutorialCategory::Optimization,
639 difficulty: DifficultyLevel::Advanced,
640 prerequisites: vec![
641 "vqe_tutorial".to_string(),
642 "Combinatorial optimization".to_string(),
643 ],
644 learning_objectives: vec![
645 "Understand QAOA algorithm structure".to_string(),
646 "Implement QAOA for MaxCut problem".to_string(),
647 "Learn about QAOA parameter optimization".to_string(),
648 "Apply QAOA to real optimization problems".to_string(),
649 ],
650 duration_minutes: 100,
651 sections: vec![TutorialSection {
652 title: "QAOA for MaxCut".to_string(),
653 content: "QAOA can solve graph partitioning problems like MaxCut approximately."
654 .to_string(),
655 code_examples: vec![CodeExample {
656 title: "MaxCut with QAOA".to_string(),
657 description: "Solve MaxCut problem using QAOA".to_string(),
658 code: r#"
659use quantrs2_ml::prelude::*;
660
661// Define a simple graph (adjacency matrix)
662let graph = Array2::from_shape_vec(
663 (4, 4),
664 vec![
665 0.0, 1.0, 1.0, 0.0,
666 1.0, 0.0, 1.0, 1.0,
667 1.0, 1.0, 0.0, 1.0,
668 0.0, 1.0, 1.0, 0.0,
669 ]
670)?;
671
672// Create QAOA instance
673let mut qaoa = QuantumMLQUBO::new(4, 2)?; // 4 qubits, 2 layers
674
675// Convert MaxCut to QUBO formulation
676let qubo_matrix = qaoa.maxcut_to_qubo(&graph)?;
677
678// Solve with quantum annealing
679let annealing_params = AnnealingParams {
680 num_reads: 1000,
681 annealing_time: 20.0,
682 temperature: 0.1,
683};
684
685let result = qaoa.solve_qubo(&qubo_matrix, annealing_params)?;
686println!("Best cut value: {}", result.energy);
687println!("Optimal partition: {:?}", result.solution);
688"#
689 .to_string(),
690 expected_output: Some(
691 "Best cut value: 4\nOptimal partition: [0, 1, 0, 1]".to_string(),
692 ),
693 explanation:
694 "This solves a graph partitioning problem using quantum optimization."
695 .to_string(),
696 }],
697 interactive_elements: vec![],
698 key_concepts: vec![
699 "QAOA approximates combinatorial optimization".to_string(),
700 "Problem encoding into quantum Hamiltonian".to_string(),
701 "Alternating mixer and problem Hamiltonians".to_string(),
702 ],
703 }],
704 exercises: vec!["qaoa_tsp".to_string()],
705 });
706
707 self.tutorials
708 .insert(TutorialCategory::Optimization, tutorials);
709 }
710
711 fn register_hybrid_tutorials(&mut self) {
713 let mut tutorials = Vec::new();
714
715 tutorials.push(Tutorial {
717 id: "hybrid_ml".to_string(),
718 title: "Hybrid Quantum-Classical Machine Learning".to_string(),
719 description: "Learn to combine quantum and classical ML techniques effectively.".to_string(),
720 category: TutorialCategory::Hybrid,
721 difficulty: DifficultyLevel::Intermediate,
722 prerequisites: vec!["qnn_intro".to_string(), "Classical ML experience".to_string()],
723 learning_objectives: vec![
724 "Design hybrid ML pipelines".to_string(),
725 "Combine quantum feature extraction with classical models".to_string(),
726 "Implement ensemble methods".to_string(),
727 "Optimize hybrid workflows".to_string(),
728 ],
729 duration_minutes: 80,
730 sections: vec![
731 TutorialSection {
732 title: "Hybrid Pipeline Design".to_string(),
733 content: "Hybrid approaches can leverage the best of both quantum and classical worlds.".to_string(),
734 code_examples: vec![
735 CodeExample {
736 title: "Quantum Feature Extraction + Classical ML".to_string(),
737 description: "Use quantum circuits for feature extraction and classical models for decision making".to_string(),
738 code: r#"
739use quantrs2_ml::prelude::*;
740
741// Create hybrid pipeline manager
742let manager = HybridPipelineManager::new();
743
744// Configure hybrid pipeline
745let config = PipelineConfig::default();
746
747// Create quantum feature extractor + classical classifier pipeline
748let mut pipeline = manager.create_pipeline("hybrid_classification", config)?;
749
750// Sample data
751let X = Array2::random((1000, 10), Uniform::new(-1.0, 1.0)?);
752let y = Array1::from_vec((0..1000).map(|i| if i < 500 { 0.0 } else { 1.0 }).collect());
753
754// Train hybrid pipeline
755pipeline.fit(&X.into_dyn(), &y.into_dyn().insert_axis(Axis(1)))?;
756
757// Make predictions
758let test_X = Array2::random((100, 10), Uniform::new(-1.0, 1.0)?);
759let predictions = pipeline.predict(&test_X.into_dyn())?;
760"#.to_string(),
761 expected_output: Some("Hybrid pipeline trained and predictions made".to_string()),
762 explanation: "This demonstrates a hybrid approach combining quantum feature learning with classical decision making.".to_string(),
763 }
764 ],
765 interactive_elements: vec![
766 InteractiveElement {
767 element_type: InteractiveType::Experiment,
768 title: "Hybrid vs Pure Quantum Comparison".to_string(),
769 instructions: "Compare performance of hybrid vs pure quantum approaches".to_string(),
770 parameters: HashMap::new(),
771 }
772 ],
773 key_concepts: vec![
774 "Hybrid methods combine strengths of both paradigms".to_string(),
775 "Quantum preprocessing can enhance classical ML".to_string(),
776 "Careful design is crucial for hybrid success".to_string(),
777 ],
778 },
779 ],
780 exercises: vec!["hybrid_credit_scoring".to_string()],
781 });
782
783 self.tutorials.insert(TutorialCategory::Hybrid, tutorials);
784 }
785
786 fn register_application_tutorials(&mut self) {
788 let mut tutorials = Vec::new();
789
790 tutorials.push(Tutorial {
792 id: "finance_qml".to_string(),
793 title: "Quantum ML for Finance".to_string(),
794 description: "Apply quantum machine learning to financial problems like portfolio optimization and risk assessment.".to_string(),
795 category: TutorialCategory::Applications,
796 difficulty: DifficultyLevel::Intermediate,
797 prerequisites: vec!["hybrid_ml".to_string(), "Finance domain knowledge".to_string()],
798 learning_objectives: vec![
799 "Apply QML to portfolio optimization".to_string(),
800 "Implement quantum risk models".to_string(),
801 "Use domain templates for finance".to_string(),
802 "Evaluate quantum advantage in finance".to_string(),
803 ],
804 duration_minutes: 95,
805 sections: vec![
806 TutorialSection {
807 title: "Quantum Portfolio Optimization".to_string(),
808 content: "Use quantum optimization for portfolio selection under constraints.".to_string(),
809 code_examples: vec![
810 CodeExample {
811 title: "Portfolio Optimization with Domain Templates".to_string(),
812 description: "Use finance domain templates for portfolio optimization".to_string(),
813 code: r#"
814use quantrs2_ml::prelude::*;
815
816// Load domain template manager
817let template_manager = DomainTemplateManager::new();
818
819// Configure portfolio optimization template
820let config = TemplateConfig {
821 num_qubits: 10,
822 input_dim: 20, // 20 assets
823 output_dim: 20, // Portfolio weights
824 parameters: HashMap::new(),
825};
826
827// Create portfolio optimization model
828let mut portfolio_model = template_manager.create_model_from_template(
829 "Portfolio Optimization",
830 config
831)?;
832
833// Sample return data (20 assets, 252 trading days)
834let returns = Array2::random((252, 20), Normal::new(0.001, 0.02)?);
835
836// Risk-return optimization
837let expected_returns = returns.mean_axis(Axis(0))
838 .ok_or_else(|| MLError::InvalidConfiguration("Failed to compute mean returns".to_string()))?;
839portfolio_model.train(&returns.into_dyn(), &expected_returns.into_dyn().insert_axis(Axis(1)))?;
840
841// Get optimal portfolio weights
842let optimal_weights = portfolio_model.predict(&expected_returns.into_dyn())?;
843"#.to_string(),
844 expected_output: Some("Optimal portfolio weights computed".to_string()),
845 explanation: "This uses quantum optimization to find optimal portfolio allocations.".to_string(),
846 }
847 ],
848 interactive_elements: vec![],
849 key_concepts: vec![
850 "Quantum optimization for constrained problems".to_string(),
851 "Risk-return trade-offs in portfolio theory".to_string(),
852 "Domain templates simplify implementation".to_string(),
853 ],
854 },
855 ],
856 exercises: vec!["portfolio_backtest".to_string()],
857 });
858
859 self.tutorials
860 .insert(TutorialCategory::Applications, tutorials);
861 }
862
863 fn register_advanced_tutorials(&mut self) {
865 let mut tutorials = Vec::new();
866
867 tutorials.push(Tutorial {
869 id: "quantum_gans".to_string(),
870 title: "Quantum Generative Adversarial Networks".to_string(),
871 description: "Implement quantum GANs for generating quantum and classical data.".to_string(),
872 category: TutorialCategory::Advanced,
873 difficulty: DifficultyLevel::Expert,
874 prerequisites: vec!["qnn_intro".to_string(), "GAN knowledge".to_string()],
875 learning_objectives: vec![
876 "Understand quantum GAN architecture".to_string(),
877 "Implement quantum generator and discriminator".to_string(),
878 "Train quantum GANs on real data".to_string(),
879 "Evaluate generated samples quality".to_string(),
880 ],
881 duration_minutes: 150,
882 sections: vec![
883 TutorialSection {
884 title: "Quantum GAN Architecture".to_string(),
885 content: "Quantum GANs use quantum circuits as generators and/or discriminators.".to_string(),
886 code_examples: vec![
887 CodeExample {
888 title: "Simple Quantum GAN".to_string(),
889 description: "Implement a basic quantum GAN".to_string(),
890 code: r#"
891use quantrs2_ml::prelude::*;
892
893// Configure quantum GAN
894let gan_config = GANConfig {
895 latent_dim: 4,
896 data_dim: 8,
897 generator_layers: 3,
898 discriminator_layers: 2,
899 learning_rate: 0.01,
900 batch_size: 32,
901 num_epochs: 100,
902};
903
904// Create enhanced quantum GAN
905let mut qgan = EnhancedQuantumGAN::new(gan_config)?;
906
907// Generate training data (simplified)
908let real_data = Array2::random((1000, 8), Normal::new(0.0, 1.0)?);
909
910// Train the quantum GAN
911qgan.train(&real_data)?;
912
913// Generate new samples
914let generated_samples = qgan.generate(100)?;
915"#.to_string(),
916 expected_output: Some("Quantum GAN trained, samples generated".to_string()),
917 explanation: "This implements a quantum GAN that can learn to generate data similar to the training set.".to_string(),
918 }
919 ],
920 interactive_elements: vec![
921 InteractiveElement {
922 element_type: InteractiveType::Visualization,
923 title: "GAN Training Dynamics".to_string(),
924 instructions: "Visualize generator and discriminator loss during training".to_string(),
925 parameters: HashMap::new(),
926 }
927 ],
928 key_concepts: vec![
929 "Adversarial training in quantum setting".to_string(),
930 "Quantum advantage in generative modeling".to_string(),
931 "Challenges in quantum GAN training".to_string(),
932 ],
933 },
934 ],
935 exercises: vec!["qgan_mnist".to_string()],
936 });
937
938 self.tutorials.insert(TutorialCategory::Advanced, tutorials);
939 }
940
941 fn register_exercises(&mut self) {
943 self.exercises.insert(
945 "qc_basic_gates".to_string(),
946 Exercise {
947 id: "qc_basic_gates".to_string(),
948 title: "Basic Quantum Gates".to_string(),
949 description:
950 "Practice applying basic quantum gates and understanding their effects"
951 .to_string(),
952 exercise_type: ExerciseType::CircuitDesign,
953 instructions: vec![
954 "Create a 2-qubit circuit".to_string(),
955 "Apply H gate to first qubit".to_string(),
956 "Apply CNOT gate with first qubit as control".to_string(),
957 "Measure both qubits".to_string(),
958 ],
959 starter_code: Some(
960 r#"
961use quantrs2_circuit::prelude::*;
962
963fn create_bell_state() -> Result<QuantumCircuit> {
964 let mut circuit = QuantumCircuit::new(2);
965
966 // Step 1: Apply Hadamard to qubit 0 to create superposition |+⟩
967 circuit.h(0);
968 // Step 2: Apply CNOT with qubit 0 as control and qubit 1 as target
969 // to entangle them, creating the Bell state (|00⟩ + |11⟩)/√2
970 circuit.cnot(0, 1);
971 // Step 3: Measure both qubits
972 circuit.measure_all();
973
974 Ok(circuit)
975}
976"#
977 .to_string(),
978 ),
979 solution_code: r#"
980use quantrs2_circuit::prelude::*;
981
982fn create_bell_state() -> Result<QuantumCircuit> {
983 let mut circuit = QuantumCircuit::new(2);
984
985 circuit.h(0);
986 circuit.cnot(0, 1);
987 circuit.measure_all();
988
989 Ok(circuit)
990}
991"#
992 .to_string(),
993 test_cases: vec![
994 TestCase {
995 description: "Circuit should have 2 qubits".to_string(),
996 input: "circuit.num_qubits()".to_string(),
997 expected_output: "2".to_string(),
998 points: 10,
999 },
1000 TestCase {
1001 description: "Circuit should create Bell state".to_string(),
1002 input: "measure_bell_state_fidelity()".to_string(),
1003 expected_output: "> 0.95".to_string(),
1004 points: 20,
1005 },
1006 ],
1007 hints: vec![
1008 "Remember that H gate creates superposition".to_string(),
1009 "CNOT gate creates entanglement between qubits".to_string(),
1010 ],
1011 },
1012 );
1013
1014 self.exercises.insert(
1016 "qnn_classification".to_string(),
1017 Exercise {
1018 id: "qnn_classification".to_string(),
1019 title: "QNN Binary Classification".to_string(),
1020 description: "Implement a quantum neural network for binary classification"
1021 .to_string(),
1022 exercise_type: ExerciseType::AlgorithmImplementation,
1023 instructions: vec![
1024 "Create a QNN with 4 qubits".to_string(),
1025 "Add embedding and entangling layers".to_string(),
1026 "Train on provided dataset".to_string(),
1027 "Achieve >85% accuracy".to_string(),
1028 ],
1029 starter_code: Some(
1030 r#"
1031use quantrs2_ml::prelude::*;
1032
1033/// QNN binary classifier implementing angle encoding + variational layers.
1034///
1035/// Architecture:
1036/// 1. Embedding layer – RY/RZ angle encoding of input features
1037/// 2. Entangling layer – CNOT chain for quantum correlations
1038/// 3. Variational layer – trainable RY/RZ parameters (gradient via parameter-shift)
1039/// 4. Measurement – Z-expectation on qubit 0 → threshold at 0 → class label
1040fn train_qnn_classifier(X: &ArrayD<f64>, y: &ArrayD<f64>) -> Result<Box<dyn QuantumModel>> {
1041 // Build a 4-qubit QNN with embedding, entangling, and parameterised layers
1042 let qnn_builder = QNNBuilder::new(4)
1043 .add_layer(QNNLayer::Embedding {
1044 rotation_gates: vec!["RY".to_string(), "RZ".to_string()],
1045 })
1046 .add_layer(QNNLayer::Entangling {
1047 entangling_gate: "CNOT".to_string(),
1048 })
1049 .add_layer(QNNLayer::Parameterized {
1050 gates: vec!["RY".to_string(), "RZ".to_string()],
1051 num_parameters: 8,
1052 });
1053
1054 let mut qnn = qnn_builder.build()?;
1055
1056 // Training loop using parameter-shift gradient descent
1057 // Parameters are updated by the QNN's internal optimizer
1058 qnn.train(X, y)?;
1059
1060 Ok(Box::new(qnn))
1061}
1062"#
1063 .to_string(),
1064 ),
1065 solution_code: r#"
1066use quantrs2_ml::prelude::*;
1067
1068fn train_qnn_classifier(X: &ArrayD<f64>, y: &ArrayD<f64>) -> Result<Box<dyn QuantumModel>> {
1069 let mut qnn_builder = QNNBuilder::new(4)
1070 .add_layer(QNNLayer::Embedding { rotation_gates: vec!["RY", "RZ"] })
1071 .add_layer(QNNLayer::Entangling { entangling_gate: "CNOT" })
1072 .add_layer(QNNLayer::Parameterized {
1073 gates: vec!["RY", "RZ"],
1074 num_parameters: 8
1075 });
1076
1077 let mut qnn = qnn_builder.build()?;
1078 qnn.train(X, y)?;
1079
1080 Ok(Box::new(qnn))
1081}
1082"#
1083 .to_string(),
1084 test_cases: vec![
1085 TestCase {
1086 description: "Model should train without errors".to_string(),
1087 input: "train_qnn_classifier(&X, &y)".to_string(),
1088 expected_output: "Ok(model)".to_string(),
1089 points: 15,
1090 },
1091 TestCase {
1092 description: "Model should achieve >85% accuracy".to_string(),
1093 input: "evaluate_accuracy(&model, &X_test, &y_test)".to_string(),
1094 expected_output: "> 0.85".to_string(),
1095 points: 25,
1096 },
1097 ],
1098 hints: vec![
1099 "Use appropriate data encoding for your problem".to_string(),
1100 "Try different ansatz architectures".to_string(),
1101 "Monitor training convergence".to_string(),
1102 ],
1103 },
1104 );
1105 }
1106
1107 pub fn get_category_tutorials(&self, category: &TutorialCategory) -> Option<&Vec<Tutorial>> {
1109 self.tutorials.get(category)
1110 }
1111
1112 pub fn get_available_categories(&self) -> Vec<TutorialCategory> {
1114 self.tutorials.keys().cloned().collect()
1115 }
1116
1117 pub fn search_by_difficulty(&self, difficulty: &DifficultyLevel) -> Vec<&Tutorial> {
1119 self.tutorials
1120 .values()
1121 .flatten()
1122 .filter(|tutorial| {
1123 std::mem::discriminant(&tutorial.difficulty) == std::mem::discriminant(difficulty)
1124 })
1125 .collect()
1126 }
1127
1128 pub fn get_tutorial(&self, tutorial_id: &str) -> Option<&Tutorial> {
1130 self.tutorials
1131 .values()
1132 .flatten()
1133 .find(|tutorial| tutorial.id == tutorial_id)
1134 }
1135
1136 pub fn get_exercise(&self, exercise_id: &str) -> Option<&Exercise> {
1138 self.exercises.get(exercise_id)
1139 }
1140
1141 pub fn start_tutorial(&mut self, user_id: String, tutorial_id: String) -> Result<()> {
1143 if !self
1144 .tutorials
1145 .values()
1146 .flatten()
1147 .any(|t| t.id == tutorial_id)
1148 {
1149 return Err(MLError::InvalidConfiguration(format!(
1150 "Tutorial not found: {}",
1151 tutorial_id
1152 )));
1153 }
1154
1155 let mut progress =
1156 self.progress
1157 .entry(user_id.clone())
1158 .or_insert_with(|| TutorialProgress {
1159 user_id: user_id.clone(),
1160 completed_tutorials: Vec::new(),
1161 completed_exercises: Vec::new(),
1162 current_tutorial: None,
1163 scores: HashMap::new(),
1164 time_spent: HashMap::new(),
1165 });
1166
1167 progress.current_tutorial = Some(tutorial_id);
1168 Ok(())
1169 }
1170
1171 pub fn complete_tutorial(
1173 &mut self,
1174 user_id: &str,
1175 tutorial_id: String,
1176 score: f64,
1177 time_minutes: usize,
1178 ) -> Result<()> {
1179 let progress = self
1180 .progress
1181 .get_mut(user_id)
1182 .ok_or_else(|| MLError::InvalidConfiguration("User not found".to_string()))?;
1183
1184 progress.completed_tutorials.push(tutorial_id.clone());
1185 progress.scores.insert(tutorial_id.clone(), score);
1186 progress.time_spent.insert(tutorial_id, time_minutes);
1187 progress.current_tutorial = None;
1188
1189 Ok(())
1190 }
1191
1192 pub fn recommend_learning_path(&self, user_background: &UserBackground) -> Vec<String> {
1194 let mut path = Vec::new();
1195
1196 match user_background.experience_level {
1197 ExperienceLevel::Beginner => {
1198 path.extend(vec![
1199 "qc_intro".to_string(),
1200 "qc_circuits".to_string(),
1201 "qnn_intro".to_string(),
1202 "qsvm_tutorial".to_string(),
1203 ]);
1204 }
1205 ExperienceLevel::Intermediate => {
1206 path.extend(vec![
1207 "qnn_intro".to_string(),
1208 "qsvm_tutorial".to_string(),
1209 "vqe_tutorial".to_string(),
1210 "hybrid_ml".to_string(),
1211 ]);
1212 }
1213 ExperienceLevel::Advanced => {
1214 path.extend(vec![
1215 "vqe_tutorial".to_string(),
1216 "qaoa_tutorial".to_string(),
1217 "hybrid_ml".to_string(),
1218 "quantum_gans".to_string(),
1219 ]);
1220 }
1221 }
1222
1223 if let Some(domain) = &user_background.target_domain {
1225 match domain.as_str() {
1226 "finance" => path.push("finance_qml".to_string()),
1227 _ => {} }
1229 }
1230
1231 path
1232 }
1233
1234 pub fn run_interactive_session(&self, tutorial_id: &str) -> Result<TutorialSession> {
1236 let tutorial = self.get_tutorial(tutorial_id).ok_or_else(|| {
1237 MLError::InvalidConfiguration(format!("Tutorial not found: {}", tutorial_id))
1238 })?;
1239
1240 Ok(TutorialSession {
1241 tutorial_id: tutorial_id.to_string(),
1242 current_section: 0,
1243 completed_sections: Vec::new(),
1244 session_start_time: std::time::SystemTime::now(),
1245 interactive_state: HashMap::new(),
1246 })
1247 }
1248}
1249
1250#[derive(Debug, Clone)]
1252pub struct UserBackground {
1253 pub experience_level: ExperienceLevel,
1255 pub classical_ml_experience: bool,
1257 pub programming_languages: Vec<String>,
1259 pub target_domain: Option<String>,
1261 pub learning_goals: Vec<String>,
1263}
1264
1265#[derive(Debug, Clone)]
1267pub enum ExperienceLevel {
1268 Beginner,
1269 Intermediate,
1270 Advanced,
1271}
1272
1273#[derive(Debug, Clone)]
1275pub struct TutorialSession {
1276 pub tutorial_id: String,
1278 pub current_section: usize,
1280 pub completed_sections: Vec<usize>,
1282 pub session_start_time: std::time::SystemTime,
1284 pub interactive_state: HashMap<String, String>,
1286}
1287
1288impl TutorialSession {
1289 pub fn current_section(&self) -> usize {
1291 self.current_section
1292 }
1293
1294 pub fn complete_section(&mut self) {
1296 if !self.completed_sections.contains(&self.current_section) {
1297 self.completed_sections.push(self.current_section);
1298 }
1299 self.current_section += 1;
1300 }
1301
1302 pub fn is_complete(&self, total_sections: usize) -> bool {
1304 self.completed_sections.len() >= total_sections
1305 }
1306
1307 pub fn total_sections(&self) -> usize {
1309 10
1312 }
1313
1314 pub fn estimated_duration(&self) -> usize {
1316 30
1319 }
1320}
1321
1322pub mod utils {
1324 use super::*;
1325
1326 pub fn create_beginner_path() -> Vec<String> {
1328 vec![
1329 "qc_intro".to_string(),
1330 "qc_circuits".to_string(),
1331 "qnn_intro".to_string(),
1332 "qsvm_tutorial".to_string(),
1333 "hybrid_ml".to_string(),
1334 ]
1335 }
1336
1337 pub fn create_advanced_path() -> Vec<String> {
1339 vec![
1340 "vqe_tutorial".to_string(),
1341 "qaoa_tutorial".to_string(),
1342 "quantum_gans".to_string(),
1343 "finance_qml".to_string(),
1344 ]
1345 }
1346
1347 pub fn generate_progress_report(progress: &TutorialProgress) -> String {
1349 let mut report = String::new();
1350 report.push_str(&format!(
1351 "Tutorial Progress Report for User: {}\n",
1352 progress.user_id
1353 ));
1354 report.push_str("=".repeat(50).as_str());
1355 report.push_str("\n\n");
1356
1357 report.push_str(&format!(
1358 "Completed Tutorials: {}\n",
1359 progress.completed_tutorials.len()
1360 ));
1361 report.push_str(&format!(
1362 "Completed Exercises: {}\n",
1363 progress.completed_exercises.len()
1364 ));
1365
1366 if let Some(current) = &progress.current_tutorial {
1367 report.push_str(&format!("Current Tutorial: {}\n", current));
1368 }
1369
1370 report.push_str("\nScores:\n");
1371 for (tutorial, score) in &progress.scores {
1372 report.push_str(&format!(" {}: {:.1}%\n", tutorial, score * 100.0));
1373 }
1374
1375 let total_time: usize = progress.time_spent.values().sum();
1376 report.push_str(&format!("\nTotal Learning Time: {} minutes\n", total_time));
1377
1378 report
1379 }
1380
1381 pub fn validate_exercise_solution(exercise: &Exercise, user_code: &str) -> ExerciseResult {
1383 let mut passed_tests = 0;
1385 let total_tests = exercise.test_cases.len();
1386
1387 if user_code.contains("TODO") {
1389 return ExerciseResult {
1390 passed: false,
1391 score: 0.0,
1392 passed_tests,
1393 total_tests,
1394 feedback: "Remove TODO comments and implement the solution".to_string(),
1395 hints_used: 0,
1396 };
1397 }
1398
1399 passed_tests = if user_code.len() > 100 {
1401 total_tests
1402 } else {
1403 total_tests / 2
1404 };
1405
1406 let score = passed_tests as f64 / total_tests as f64;
1407 let passed = score >= 0.7;
1408
1409 ExerciseResult {
1410 passed,
1411 score,
1412 passed_tests,
1413 total_tests,
1414 feedback: if passed {
1415 "Great job! All tests passed.".to_string()
1416 } else {
1417 "Some tests failed. Check the hints and try again.".to_string()
1418 },
1419 hints_used: 0,
1420 }
1421 }
1422}
1423
1424#[derive(Debug, Clone)]
1426pub struct ExerciseResult {
1427 pub passed: bool,
1429 pub score: f64,
1431 pub passed_tests: usize,
1433 pub total_tests: usize,
1435 pub feedback: String,
1437 pub hints_used: usize,
1439}
1440
1441#[cfg(test)]
1442mod tests {
1443 use super::*;
1444
1445 #[test]
1446 fn test_tutorial_manager_creation() {
1447 let manager = TutorialManager::new();
1448 assert!(!manager.get_available_categories().is_empty());
1449 }
1450
1451 #[test]
1452 fn test_get_tutorial() {
1453 let manager = TutorialManager::new();
1454 let tutorial = manager.get_tutorial("qc_intro");
1455 assert!(tutorial.is_some());
1456 assert_eq!(
1457 tutorial.expect("Tutorial should exist").title,
1458 "Introduction to Quantum Computing"
1459 );
1460 }
1461
1462 #[test]
1463 fn test_difficulty_search() {
1464 let manager = TutorialManager::new();
1465 let beginner_tutorials = manager.search_by_difficulty(&DifficultyLevel::Beginner);
1466 assert!(!beginner_tutorials.is_empty());
1467
1468 for tutorial in beginner_tutorials {
1469 assert!(matches!(tutorial.difficulty, DifficultyLevel::Beginner));
1470 }
1471 }
1472
1473 #[test]
1474 fn test_learning_path_recommendation() {
1475 let manager = TutorialManager::new();
1476 let background = UserBackground {
1477 experience_level: ExperienceLevel::Beginner,
1478 classical_ml_experience: true,
1479 programming_languages: vec!["Python".to_string(), "Rust".to_string()],
1480 target_domain: Some("finance".to_string()),
1481 learning_goals: vec!["Learn quantum ML basics".to_string()],
1482 };
1483
1484 let path = manager.recommend_learning_path(&background);
1485 assert!(!path.is_empty());
1486 assert!(path.contains(&"qc_intro".to_string()));
1487 }
1488
1489 #[test]
1490 fn test_tutorial_progress() {
1491 let mut manager = TutorialManager::new();
1492 let user_id = "test_user".to_string();
1493 let tutorial_id = "qc_intro".to_string();
1494
1495 manager
1497 .start_tutorial(user_id.clone(), tutorial_id.clone())
1498 .expect("Should start tutorial successfully");
1499
1500 manager
1502 .complete_tutorial(&user_id, tutorial_id.clone(), 0.95, 45)
1503 .expect("Should complete tutorial successfully");
1504
1505 let progress = manager
1506 .progress
1507 .get(&user_id)
1508 .expect("User progress should exist");
1509 assert!(progress.completed_tutorials.contains(&tutorial_id));
1510 assert_eq!(progress.scores.get(&tutorial_id), Some(&0.95));
1511 }
1512
1513 #[test]
1514 fn test_exercise_validation() {
1515 let manager = TutorialManager::new();
1516 let exercise = manager
1517 .get_exercise("qc_basic_gates")
1518 .expect("Exercise should exist");
1519
1520 let good_solution = r#"
1521 use quantrs2_circuit::prelude::*;
1522
1523 fn create_bell_state() -> Result<QuantumCircuit> {
1524 let mut circuit = QuantumCircuit::new(2);
1525 circuit.h(0);
1526 circuit.cnot(0, 1);
1527 circuit.measure_all();
1528 Ok(circuit)
1529 }
1530 "#;
1531
1532 let result = utils::validate_exercise_solution(exercise, good_solution);
1533 assert!(result.passed);
1534 assert!(result.score > 0.7);
1535 }
1536
1537 #[test]
1538 fn test_interactive_session() {
1539 let manager = TutorialManager::new();
1540 let session = manager
1541 .run_interactive_session("qc_intro")
1542 .expect("Should create interactive session");
1543
1544 assert_eq!(session.tutorial_id, "qc_intro");
1545 assert_eq!(session.current_section, 0);
1546 assert!(session.completed_sections.is_empty());
1547 }
1548}