1use crate::error::{QuantRS2Error, QuantRS2Result};
7use scirs2_core::ndarray::Array1;
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum IonSpecies {
14 Be9,
16 Ca40,
18 Ca43,
20 Yb171,
22 Ba137,
24 Sr88,
26}
27
28impl IonSpecies {
29 pub const fn atomic_mass(&self) -> f64 {
31 match self {
32 Self::Be9 => 9.012,
33 Self::Ca40 => 39.963,
34 Self::Ca43 => 42.959,
35 Self::Yb171 => 170.936,
36 Self::Ba137 => 136.906,
37 Self::Sr88 => 87.906,
38 }
39 }
40
41 pub const fn typical_trap_frequency(&self) -> f64 {
43 match self {
44 Self::Be9 => 2.0e6, Self::Ca40 | Self::Ca43 => 1.5e6, Self::Yb171 => 1.0e6, Self::Ba137 => 0.8e6, Self::Sr88 => 1.2e6, }
50 }
51
52 pub const fn qubit_wavelength(&self) -> f64 {
54 match self {
55 Self::Be9 => 313.0, Self::Ca40 | Self::Ca43 => 729.0, Self::Yb171 => 435.5, Self::Ba137 => 455.4, Self::Sr88 => 674.0, }
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum IonLevel {
67 Ground,
69 Excited,
71 Auxiliary,
73}
74
75#[derive(Debug, Clone)]
77pub struct MotionalMode {
78 pub mode_id: usize,
80 pub frequency: f64,
82 pub direction: String,
84 pub mode_type: MotionalModeType,
86 pub lamb_dicke_parameter: f64,
88 pub phonon_state: Array1<Complex64>,
90 pub max_phonons: usize,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum MotionalModeType {
96 CenterOfMass,
98 Breathing,
100 Rocking,
102 HigherOrder,
104}
105
106impl MotionalMode {
107 pub fn ground_state(
109 mode_id: usize,
110 frequency: f64,
111 direction: String,
112 mode_type: MotionalModeType,
113 lamb_dicke_parameter: f64,
114 max_phonons: usize,
115 ) -> Self {
116 let mut phonon_state = Array1::zeros(max_phonons + 1);
117 phonon_state[0] = Complex64::new(1.0, 0.0); Self {
120 mode_id,
121 frequency,
122 direction,
123 mode_type,
124 lamb_dicke_parameter,
125 phonon_state,
126 max_phonons,
127 }
128 }
129
130 pub fn thermal_state(
132 mode_id: usize,
133 frequency: f64,
134 direction: String,
135 mode_type: MotionalModeType,
136 lamb_dicke_parameter: f64,
137 max_phonons: usize,
138 mean_phonons: f64,
139 ) -> Self {
140 let mut phonon_state = Array1::zeros(max_phonons + 1);
141
142 let nbar = mean_phonons;
144 for n in 0..=max_phonons {
145 let prob = nbar.powi(n as i32) / (1.0 + nbar).powi(n as i32 + 1);
146 phonon_state[n] = Complex64::new(prob.sqrt(), 0.0);
147 }
148
149 Self {
150 mode_id,
151 frequency,
152 direction,
153 mode_type,
154 lamb_dicke_parameter,
155 phonon_state,
156 max_phonons,
157 }
158 }
159
160 pub fn mean_phonon_number(&self) -> f64 {
162 let mut mean = 0.0;
163 for n in 0..=self.max_phonons {
164 mean += (n as f64) * self.phonon_state[n].norm_sqr();
165 }
166 mean
167 }
168
169 pub fn displace(&mut self, alpha: Complex64) -> QuantRS2Result<()> {
171 let mut new_state = Array1::zeros(self.max_phonons + 1);
172
173 for n in 0..=self.max_phonons {
176 new_state[n] = self.phonon_state[n] * (-alpha.norm_sqr() / 2.0).exp();
177
178 if n > 0 {
180 new_state[n] += alpha * (n as f64).sqrt() * self.phonon_state[n - 1];
181 }
182 }
183
184 let norm = new_state
186 .iter()
187 .map(|x: &Complex64| x.norm_sqr())
188 .sum::<f64>()
189 .sqrt();
190 for amp in &mut new_state {
191 *amp /= norm;
192 }
193
194 self.phonon_state = new_state;
195 Ok(())
196 }
197}
198
199#[derive(Debug, Clone)]
201pub struct TrappedIon {
202 pub ion_id: usize,
204 pub species: IonSpecies,
206 pub position: [f64; 3],
208 pub electronic_state: Array1<Complex64>,
210 pub motional_coupling: HashMap<usize, f64>,
212}
213
214impl TrappedIon {
215 pub fn new(ion_id: usize, species: IonSpecies, position: [f64; 3]) -> Self {
217 let mut electronic_state = Array1::zeros(2);
218 electronic_state[0] = Complex64::new(1.0, 0.0); Self {
221 ion_id,
222 species,
223 position,
224 electronic_state,
225 motional_coupling: HashMap::new(),
226 }
227 }
228
229 pub fn set_state(&mut self, amplitudes: [Complex64; 2]) -> QuantRS2Result<()> {
231 let norm = (amplitudes[0].norm_sqr() + amplitudes[1].norm_sqr()).sqrt();
233 if norm < 1e-10 {
234 return Err(QuantRS2Error::InvalidInput(
235 "State cannot have zero norm".to_string(),
236 ));
237 }
238
239 self.electronic_state[0] = amplitudes[0] / norm;
240 self.electronic_state[1] = amplitudes[1] / norm;
241
242 Ok(())
243 }
244
245 pub fn get_state(&self) -> [Complex64; 2] {
247 [self.electronic_state[0], self.electronic_state[1]]
248 }
249}
250
251#[derive(Debug, Clone)]
253pub struct LaserPulse {
254 pub frequency: f64,
256 pub rabi_frequency: f64,
258 pub duration: f64,
260 pub phase: f64,
262 pub detuning: f64,
264 pub target_ions: Vec<usize>,
266 pub addressing_efficiency: f64,
268}
269
270impl LaserPulse {
271 pub const fn carrier_pulse(
273 rabi_frequency: f64,
274 duration: f64,
275 phase: f64,
276 target_ions: Vec<usize>,
277 ) -> Self {
278 Self {
279 frequency: 0.0, rabi_frequency,
281 duration,
282 phase,
283 detuning: 0.0, target_ions,
285 addressing_efficiency: 1.0,
286 }
287 }
288
289 pub fn red_sideband_pulse(
291 rabi_frequency: f64,
292 duration: f64,
293 phase: f64,
294 target_ions: Vec<usize>,
295 motional_frequency: f64,
296 ) -> Self {
297 Self {
298 frequency: 0.0,
299 rabi_frequency,
300 duration,
301 phase,
302 detuning: -motional_frequency, target_ions,
304 addressing_efficiency: 1.0,
305 }
306 }
307
308 pub const fn blue_sideband_pulse(
310 rabi_frequency: f64,
311 duration: f64,
312 phase: f64,
313 target_ions: Vec<usize>,
314 motional_frequency: f64,
315 ) -> Self {
316 Self {
317 frequency: 0.0,
318 rabi_frequency,
319 duration,
320 phase,
321 detuning: motional_frequency, target_ions,
323 addressing_efficiency: 1.0,
324 }
325 }
326}
327
328#[derive(Debug, Clone)]
330pub struct TrappedIonSystem {
331 pub num_ions: usize,
333 pub ions: Vec<TrappedIon>,
335 pub motional_modes: Vec<MotionalMode>,
337 pub global_state: Option<Array1<Complex64>>,
339 pub temperature: f64,
341 pub magnetic_field: f64,
343}
344
345impl TrappedIonSystem {
346 pub fn new(ion_species: Vec<IonSpecies>, positions: Vec<[f64; 3]>) -> QuantRS2Result<Self> {
348 if ion_species.len() != positions.len() {
349 return Err(QuantRS2Error::InvalidInput(
350 "Number of species and positions must match".to_string(),
351 ));
352 }
353
354 let num_ions = ion_species.len();
355 let ions: Vec<TrappedIon> = ion_species
356 .into_iter()
357 .zip(positions)
358 .enumerate()
359 .map(|(id, (species, pos))| TrappedIon::new(id, species, pos))
360 .collect();
361
362 let mut motional_modes = Vec::new();
364 for i in 0..3 {
365 let direction = match i {
366 0 => "x".to_string(),
367 1 => "y".to_string(),
368 2 => "z".to_string(),
369 _ => unreachable!(),
370 };
371
372 let mode = MotionalMode::ground_state(
373 i,
374 1.0e6, direction,
376 MotionalModeType::CenterOfMass,
377 0.1, 20, );
380 motional_modes.push(mode);
381 }
382
383 Ok(Self {
384 num_ions,
385 ions,
386 motional_modes,
387 global_state: None,
388 temperature: 1e-6, magnetic_field: 0.01, })
391 }
392
393 pub fn apply_laser_pulse(&mut self, pulse: &LaserPulse) -> QuantRS2Result<()> {
395 for &ion_id in &pulse.target_ions {
396 if ion_id >= self.num_ions {
397 return Err(QuantRS2Error::InvalidInput(
398 "Target ion ID out of bounds".to_string(),
399 ));
400 }
401
402 self.apply_pulse_to_ion(ion_id, pulse)?;
403 }
404
405 Ok(())
406 }
407
408 fn apply_pulse_to_ion(&mut self, ion_id: usize, pulse: &LaserPulse) -> QuantRS2Result<()> {
410 let ion = &mut self.ions[ion_id];
411
412 let theta = pulse.rabi_frequency * pulse.duration * pulse.addressing_efficiency;
414
415 let cos_half = (theta / 2.0).cos();
417 let sin_half = (theta / 2.0).sin();
418 let phase_factor = Complex64::new(0.0, pulse.phase).exp();
419
420 let old_state = ion.electronic_state.clone();
422 ion.electronic_state[0] = cos_half * old_state[0]
423 - Complex64::new(0.0, 1.0) * sin_half * phase_factor * old_state[1];
424 ion.electronic_state[1] = cos_half * old_state[1]
425 - Complex64::new(0.0, 1.0) * sin_half * phase_factor.conj() * old_state[0];
426
427 if pulse.detuning.abs() > 1e3 {
429 self.apply_motional_coupling(ion_id, pulse)?;
430 }
431
432 Ok(())
433 }
434
435 fn apply_motional_coupling(
437 &mut self,
438 _ion_id: usize,
439 pulse: &LaserPulse,
440 ) -> QuantRS2Result<()> {
441 let mut mode_idx = None;
443 let mut eta = 0.0;
444
445 for (i, mode) in self.motional_modes.iter().enumerate() {
446 if (mode.frequency - pulse.detuning.abs()).abs() < 1e3 {
447 mode_idx = Some(i);
448 eta = mode.lamb_dicke_parameter;
449 break;
450 }
451 }
452
453 if let Some(idx) = mode_idx {
454 let mode = &mut self.motional_modes[idx];
455 if pulse.detuning < 0.0 {
456 let mut new_state = Array1::zeros(mode.max_phonons + 1);
458
459 for n in 1..=mode.max_phonons {
460 let coupling = eta * (n as f64).sqrt();
462 new_state[n - 1] += coupling * mode.phonon_state[n];
463 }
464
465 let norm = new_state
467 .iter()
468 .map(|x: &Complex64| x.norm_sqr())
469 .sum::<f64>()
470 .sqrt();
471 if norm > 1e-10 {
472 for amp in &mut new_state {
473 *amp /= norm;
474 }
475 mode.phonon_state = new_state;
476 }
477 } else {
478 let mut new_state = Array1::zeros(mode.max_phonons + 1);
480
481 for n in 0..mode.max_phonons {
482 let coupling = eta * ((n + 1) as f64).sqrt();
484 new_state[n + 1] += coupling * mode.phonon_state[n];
485 }
486
487 let norm = new_state
489 .iter()
490 .map(|x: &Complex64| x.norm_sqr())
491 .sum::<f64>()
492 .sqrt();
493 if norm > 1e-10 {
494 for amp in &mut new_state {
495 *amp /= norm;
496 }
497 mode.phonon_state = new_state;
498 }
499 }
500 }
501
502 Ok(())
503 }
504
505 fn apply_red_sideband(&self, mode: &mut MotionalMode, eta: f64) -> QuantRS2Result<()> {
507 let mut new_state = Array1::zeros(mode.max_phonons + 1);
508
509 for n in 1..=mode.max_phonons {
510 let coupling = eta * (n as f64).sqrt();
512 new_state[n - 1] += coupling * mode.phonon_state[n];
513 }
514
515 let norm = new_state
517 .iter()
518 .map(|x: &Complex64| x.norm_sqr())
519 .sum::<f64>()
520 .sqrt();
521 if norm > 1e-10 {
522 for amp in &mut new_state {
523 *amp /= norm;
524 }
525 mode.phonon_state = new_state;
526 }
527
528 Ok(())
529 }
530
531 fn apply_blue_sideband(&self, mode: &mut MotionalMode, eta: f64) -> QuantRS2Result<()> {
533 let mut new_state = Array1::zeros(mode.max_phonons + 1);
534
535 for n in 0..mode.max_phonons {
536 let coupling = eta * ((n + 1) as f64).sqrt();
538 new_state[n + 1] += coupling * mode.phonon_state[n];
539 }
540
541 let norm = new_state
543 .iter()
544 .map(|x: &Complex64| x.norm_sqr())
545 .sum::<f64>()
546 .sqrt();
547 if norm > 1e-10 {
548 for amp in &mut new_state {
549 *amp /= norm;
550 }
551 mode.phonon_state = new_state;
552 }
553
554 Ok(())
555 }
556
557 pub fn molmer_sorensen_gate(
559 &mut self,
560 ion1: usize,
561 ion2: usize,
562 phase: f64,
563 ) -> QuantRS2Result<()> {
564 if ion1 >= self.num_ions || ion2 >= self.num_ions {
565 return Err(QuantRS2Error::InvalidInput(
566 "Ion index out of bounds".to_string(),
567 ));
568 }
569
570 if ion1 == ion2 {
571 return Err(QuantRS2Error::InvalidInput(
572 "Cannot apply MS gate to same ion".to_string(),
573 ));
574 }
575
576 let state1 = self.ions[ion1].get_state();
581 let state2 = self.ions[ion2].get_state();
582
583 if state1[0].norm() > 0.9 && state2[0].norm() > 0.9 {
585 let sqrt2_inv = 1.0 / std::f64::consts::SQRT_2;
587 let phase_factor = Complex64::new(0.0, phase);
588
589 self.ions[ion1].set_state([
590 sqrt2_inv * Complex64::new(1.0, 0.0),
591 sqrt2_inv * phase_factor,
592 ])?;
593 self.ions[ion2].set_state([
594 sqrt2_inv * Complex64::new(1.0, 0.0),
595 sqrt2_inv * phase_factor,
596 ])?;
597 } else {
598 let sqrt2_inv = 1.0 / std::f64::consts::SQRT_2;
600
601 let new_state1_0 = sqrt2_inv * (state1[0] + state2[1]);
603 let new_state1_1 = sqrt2_inv * (state1[1] + state2[0]);
604 let new_state2_0 = sqrt2_inv * (state2[0] + state1[1]);
605 let new_state2_1 = sqrt2_inv * (state2[1] + state1[0]);
606
607 self.ions[ion1].set_state([new_state1_0, new_state1_1])?;
608 self.ions[ion2].set_state([new_state2_0, new_state2_1])?;
609 }
610
611 Ok(())
612 }
613
614 pub fn state_dependent_force(
616 &mut self,
617 target_ions: &[usize],
618 mode_id: usize,
619 force_strength: f64,
620 ) -> QuantRS2Result<()> {
621 if mode_id >= self.motional_modes.len() {
622 return Err(QuantRS2Error::InvalidInput(
623 "Mode ID out of bounds".to_string(),
624 ));
625 }
626
627 for &ion_id in target_ions {
629 if ion_id >= self.num_ions {
630 return Err(QuantRS2Error::InvalidInput(
631 "Ion ID out of bounds".to_string(),
632 ));
633 }
634
635 let ion_state = self.ions[ion_id].get_state();
636 let excited_population = ion_state[1].norm_sqr();
637
638 let alpha = Complex64::new(force_strength * excited_population, 0.0);
640 self.motional_modes[mode_id].displace(alpha)?;
641 }
642
643 Ok(())
644 }
645
646 pub fn measure_ion(&mut self, ion_id: usize) -> QuantRS2Result<bool> {
648 if ion_id >= self.num_ions {
649 return Err(QuantRS2Error::InvalidInput(
650 "Ion ID out of bounds".to_string(),
651 ));
652 }
653
654 let ion = &mut self.ions[ion_id];
655 let prob_excited = ion.electronic_state[1].norm_sqr();
656
657 use scirs2_core::random::prelude::*;
659 let mut rng = thread_rng();
660 let random_value: f64 = rng.gen();
661
662 let result = random_value < prob_excited;
663
664 if result {
666 ion.set_state([Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])?;
667 } else {
669 ion.set_state([Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])?;
670 }
672
673 Ok(result)
674 }
675
676 pub fn cool_motional_mode(&mut self, mode_id: usize) -> QuantRS2Result<()> {
678 if mode_id >= self.motional_modes.len() {
679 return Err(QuantRS2Error::InvalidInput(
680 "Mode ID out of bounds".to_string(),
681 ));
682 }
683
684 let mode = &mut self.motional_modes[mode_id];
685 mode.phonon_state.fill(Complex64::new(0.0, 0.0));
686 mode.phonon_state[0] = Complex64::new(1.0, 0.0);
687
688 Ok(())
689 }
690
691 pub fn get_motional_temperature(&self) -> f64 {
693 let total_energy: f64 = self
694 .motional_modes
695 .iter()
696 .map(|mode| mode.mean_phonon_number() * mode.frequency)
697 .sum();
698
699 let k_b = 1.381e-23; let avg_frequency = 1e6; total_energy / (k_b * avg_frequency * self.motional_modes.len() as f64)
704 }
705}
706
707pub struct TrappedIonGates;
709
710impl TrappedIonGates {
711 pub fn rotation_gate(
713 system: &mut TrappedIonSystem,
714 ion_id: usize,
715 axis: &str,
716 angle: f64,
717 ) -> QuantRS2Result<()> {
718 let rabi_freq = 1e6; let duration = angle / rabi_freq; let phase = match axis {
722 "x" | "z" => 0.0, "y" => std::f64::consts::PI / 2.0,
724 _ => {
725 return Err(QuantRS2Error::InvalidInput(
726 "Invalid rotation axis".to_string(),
727 ))
728 }
729 };
730
731 let pulse = LaserPulse::carrier_pulse(rabi_freq, duration, phase, vec![ion_id]);
732 system.apply_laser_pulse(&pulse)
733 }
734
735 pub fn hadamard(system: &mut TrappedIonSystem, ion_id: usize) -> QuantRS2Result<()> {
737 if ion_id >= system.num_ions {
738 return Err(QuantRS2Error::InvalidInput(
739 "Ion ID out of bounds".to_string(),
740 ));
741 }
742
743 let ion = &mut system.ions[ion_id];
745 let old_state = ion.electronic_state.clone();
746 let inv_sqrt2 = 1.0 / std::f64::consts::SQRT_2;
747
748 ion.electronic_state[0] = inv_sqrt2 * (old_state[0] + old_state[1]);
749 ion.electronic_state[1] = inv_sqrt2 * (old_state[0] - old_state[1]);
750
751 Ok(())
752 }
753
754 pub fn pauli_x(system: &mut TrappedIonSystem, ion_id: usize) -> QuantRS2Result<()> {
756 Self::rotation_gate(system, ion_id, "x", std::f64::consts::PI)
757 }
758
759 pub fn pauli_y(system: &mut TrappedIonSystem, ion_id: usize) -> QuantRS2Result<()> {
761 Self::rotation_gate(system, ion_id, "y", std::f64::consts::PI)
762 }
763
764 pub fn pauli_z(system: &mut TrappedIonSystem, ion_id: usize) -> QuantRS2Result<()> {
766 Self::rotation_gate(system, ion_id, "z", std::f64::consts::PI)
767 }
768
769 pub fn cnot(
771 system: &mut TrappedIonSystem,
772 control: usize,
773 target: usize,
774 ) -> QuantRS2Result<()> {
775 Self::rotation_gate(system, target, "y", -std::f64::consts::PI / 2.0)?;
777 system.molmer_sorensen_gate(control, target, std::f64::consts::PI / 2.0)?;
778 Self::rotation_gate(system, control, "x", -std::f64::consts::PI / 2.0)?;
779 Self::rotation_gate(system, target, "x", -std::f64::consts::PI / 2.0)?;
780 Ok(())
781 }
782
783 pub fn cz(system: &mut TrappedIonSystem, control: usize, target: usize) -> QuantRS2Result<()> {
785 Self::hadamard(system, target)?;
787 Self::cnot(system, control, target)?;
788 Self::hadamard(system, target)
789 }
790
791 pub fn toffoli(
793 system: &mut TrappedIonSystem,
794 control1: usize,
795 control2: usize,
796 target: usize,
797 ) -> QuantRS2Result<()> {
798 Self::hadamard(system, target)?;
800 Self::cnot(system, control2, target)?;
801 Self::rotation_gate(system, target, "z", -std::f64::consts::PI / 4.0)?; Self::cnot(system, control1, target)?;
803 Self::rotation_gate(system, target, "z", std::f64::consts::PI / 4.0)?; Self::cnot(system, control2, target)?;
805 Self::rotation_gate(system, target, "z", -std::f64::consts::PI / 4.0)?; Self::cnot(system, control1, target)?;
807 Self::rotation_gate(system, control1, "z", std::f64::consts::PI / 4.0)?; Self::rotation_gate(system, control2, "z", std::f64::consts::PI / 4.0)?; Self::rotation_gate(system, target, "z", std::f64::consts::PI / 4.0)?; Self::hadamard(system, target)?;
811 Self::cnot(system, control1, control2)?;
812 Self::rotation_gate(system, control1, "z", std::f64::consts::PI / 4.0)?; Self::rotation_gate(system, control2, "z", -std::f64::consts::PI / 4.0)?; Self::cnot(system, control1, control2)
815 }
816}
817
818#[cfg(test)]
819mod tests {
820 use super::*;
821
822 #[test]
823 fn test_ion_species_properties() {
824 let be9 = IonSpecies::Be9;
825 assert!((be9.atomic_mass() - 9.012).abs() < 0.001);
826 assert!(be9.typical_trap_frequency() > 1e6);
827 assert!(be9.qubit_wavelength() > 300.0);
828 }
829
830 #[test]
831 fn test_motional_mode_creation() {
832 let mode = MotionalMode::ground_state(
833 0,
834 1e6,
835 "x".to_string(),
836 MotionalModeType::CenterOfMass,
837 0.1,
838 10,
839 );
840
841 assert_eq!(mode.mode_id, 0);
842 assert!((mode.mean_phonon_number() - 0.0).abs() < 1e-10);
843 assert!((mode.phonon_state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
844 }
845
846 #[test]
847 fn test_thermal_state() {
848 let mode = MotionalMode::thermal_state(
849 0,
850 1e6,
851 "x".to_string(),
852 MotionalModeType::CenterOfMass,
853 0.1,
854 10,
855 2.0, );
857
858 let mean_phonons = mode.mean_phonon_number();
859 assert!((mean_phonons - 2.0).abs() < 0.5); }
861
862 #[test]
863 fn test_trapped_ion_creation() {
864 let ion = TrappedIon::new(0, IonSpecies::Ca40, [0.0, 0.0, 0.0]);
865 assert_eq!(ion.ion_id, 0);
866 assert_eq!(ion.species, IonSpecies::Ca40);
867
868 let state = ion.get_state();
869 assert!((state[0] - Complex64::new(1.0, 0.0)).norm() < 1e-10);
870 assert!((state[1] - Complex64::new(0.0, 0.0)).norm() < 1e-10);
871 }
872
873 #[test]
874 fn test_trapped_ion_system() {
875 let species = vec![IonSpecies::Ca40, IonSpecies::Ca40];
876 let positions = vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]];
877
878 let system = TrappedIonSystem::new(species, positions)
879 .expect("Trapped ion system should be created successfully");
880 assert_eq!(system.num_ions, 2);
881 assert_eq!(system.ions.len(), 2);
882 assert!(system.motional_modes.len() >= 3);
883 }
884
885 #[test]
886 fn test_laser_pulse_application() {
887 let species = vec![IonSpecies::Ca40];
888 let positions = vec![[0.0, 0.0, 0.0]];
889 let mut system = TrappedIonSystem::new(species, positions)
890 .expect("Trapped ion system should be created successfully");
891
892 let rabi_freq = 1e6; let pulse = LaserPulse::carrier_pulse(
895 rabi_freq, std::f64::consts::PI / rabi_freq, 0.0, vec![0], );
900
901 system
902 .apply_laser_pulse(&pulse)
903 .expect("Laser pulse should be applied successfully");
904
905 let state = system.ions[0].get_state();
907 assert!(state[1].norm() > 0.9); }
909
910 #[test]
911 fn test_motional_displacement() {
912 let mut mode = MotionalMode::ground_state(
913 0,
914 1e6,
915 "x".to_string(),
916 MotionalModeType::CenterOfMass,
917 0.1,
918 10,
919 );
920
921 let alpha = Complex64::new(1.0, 0.0);
922 mode.displace(alpha)
923 .expect("Displacement operation should succeed");
924
925 let mean_phonons = mode.mean_phonon_number();
926 assert!(mean_phonons > 0.5); }
928
929 #[test]
930 fn test_molmer_sorensen_gate() {
931 let species = vec![IonSpecies::Ca40, IonSpecies::Ca40];
932 let positions = vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]];
933 let mut system = TrappedIonSystem::new(species, positions)
934 .expect("Trapped ion system should be created successfully");
935
936 system
938 .molmer_sorensen_gate(0, 1, std::f64::consts::PI / 2.0)
939 .expect("Molmer-Sorensen gate should be applied successfully");
940
941 let state1 = system.ions[0].get_state();
943 let state2 = system.ions[1].get_state();
944
945 assert!(state1[0].norm() < 1.0);
947 assert!(state2[0].norm() < 1.0);
948 }
949
950 #[test]
951 fn test_ion_measurement() {
952 let species = vec![IonSpecies::Ca40];
953 let positions = vec![[0.0, 0.0, 0.0]];
954 let mut system = TrappedIonSystem::new(species, positions)
955 .expect("Trapped ion system should be created successfully");
956
957 system.ions[0]
959 .set_state([
960 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
961 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
962 ])
963 .expect("Ion state should be set successfully");
964
965 let result = system
966 .measure_ion(0)
967 .expect("Ion measurement should succeed");
968
969 let _ = result;
972
973 let state = system.ions[0].get_state();
975 assert!(state[0].norm() == 1.0 || state[1].norm() == 1.0);
976 }
977
978 #[test]
979 fn test_trapped_ion_gates() {
980 let species = vec![IonSpecies::Ca40, IonSpecies::Ca40];
981 let positions = vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]];
982 let mut system = TrappedIonSystem::new(species, positions)
983 .expect("Trapped ion system should be created successfully");
984
985 TrappedIonGates::pauli_x(&mut system, 0)
987 .expect("Pauli-X gate should be applied successfully");
988 let state = system.ions[0].get_state();
989 assert!(state[1].norm() > 0.9); TrappedIonGates::hadamard(&mut system, 0)
993 .expect("Hadamard gate should be applied successfully");
994 let state = system.ions[0].get_state();
995 assert!(state[0].norm() > 0.05 && state[0].norm() < 0.95); }
997
998 #[test]
999 fn test_cnot_gate() {
1000 let species = vec![IonSpecies::Ca40, IonSpecies::Ca40];
1001 let positions = vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0]];
1002 let mut system = TrappedIonSystem::new(species, positions)
1003 .expect("Trapped ion system should be created successfully");
1004
1005 TrappedIonGates::pauli_x(&mut system, 0)
1007 .expect("Pauli-X gate should be applied successfully");
1008
1009 TrappedIonGates::cnot(&mut system, 0, 1).expect("CNOT gate should be applied successfully");
1011
1012 let target_state = system.ions[1].get_state();
1014 assert!(target_state[1].norm() > 0.5);
1015 }
1016
1017 #[test]
1018 fn test_motional_temperature() {
1019 let species = vec![IonSpecies::Ca40];
1020 let positions = vec![[0.0, 0.0, 0.0]];
1021 let system = TrappedIonSystem::new(species, positions)
1022 .expect("Trapped ion system should be created successfully");
1023
1024 let temp = system.get_motional_temperature();
1025 assert!(temp >= 0.0);
1026 assert!(temp.is_finite());
1027 }
1028}