1use crate::error::{Result, SimulatorError};
17
18use scirs2_core::ndarray::*; use scirs2_core::{Complex64, Float};
21
22pub type StateIndex = usize;
24
25pub type QubitIndex = usize;
27
28#[derive(Clone)]
33pub struct QulacsStateVector {
34 state: Array1<Complex64>,
36 num_qubits: usize,
38 dim: StateIndex,
40}
41
42impl QulacsStateVector {
43 pub fn new(num_qubits: usize) -> Result<Self> {
53 if num_qubits == 0 {
54 return Err(SimulatorError::InvalidQubitCount(
55 "Number of qubits must be positive".to_string(),
56 ));
57 }
58
59 if num_qubits > 30 {
60 return Err(SimulatorError::InvalidQubitCount(format!(
61 "Number of qubits ({}) exceeds maximum (30)",
62 num_qubits
63 )));
64 }
65
66 let dim = 1 << num_qubits;
67 let mut state = Array1::<Complex64>::zeros(dim);
68 state[0] = Complex64::new(1.0, 0.0);
69
70 Ok(Self {
71 state,
72 num_qubits,
73 dim,
74 })
75 }
76
77 pub fn from_amplitudes(amplitudes: Array1<Complex64>) -> Result<Self> {
87 let dim = amplitudes.len();
88 if dim == 0 || (dim & (dim - 1)) != 0 {
89 return Err(SimulatorError::InvalidState(
90 "Dimension must be a power of 2".to_string(),
91 ));
92 }
93
94 let num_qubits = dim.trailing_zeros() as usize;
95
96 Ok(Self {
97 state: amplitudes,
98 num_qubits,
99 dim,
100 })
101 }
102
103 #[inline]
105 pub fn num_qubits(&self) -> usize {
106 self.num_qubits
107 }
108
109 #[inline]
111 pub fn dim(&self) -> StateIndex {
112 self.dim
113 }
114
115 #[inline]
117 pub fn amplitudes(&self) -> &Array1<Complex64> {
118 &self.state
119 }
120
121 #[inline]
123 pub fn amplitudes_mut(&mut self) -> &mut Array1<Complex64> {
124 &mut self.state
125 }
126
127 pub fn norm_squared(&self) -> f64 {
131 self.state.iter().map(|amp| amp.norm_sqr()).sum()
133 }
134
135 pub fn normalize(&mut self) -> Result<()> {
139 let norm = self.norm_squared().sqrt();
140 if norm < 1e-15 {
141 return Err(SimulatorError::InvalidState(
142 "Cannot normalize zero state".to_string(),
143 ));
144 }
145
146 let scale = 1.0 / norm;
147 self.state.mapv_inplace(|amp| amp * scale);
149 Ok(())
150 }
151
152 pub fn inner_product(&self, other: &Self) -> Result<Complex64> {
156 if self.dim != other.dim {
157 return Err(SimulatorError::InvalidOperation(
158 "State vectors must have the same dimension".to_string(),
159 ));
160 }
161
162 let result: Complex64 = self
164 .state
165 .iter()
166 .zip(other.state.iter())
167 .map(|(a, b)| a.conj() * b)
168 .sum();
169
170 Ok(result)
171 }
172
173 pub fn reset(&mut self) {
175 self.state.fill(Complex64::new(0.0, 0.0));
176 self.state[0] = Complex64::new(1.0, 0.0);
177 }
178
179 pub fn probability_one(&self, target: QubitIndex) -> Result<f64> {
191 if target >= self.num_qubits {
192 return Err(SimulatorError::InvalidQubitIndex {
193 index: target,
194 num_qubits: self.num_qubits,
195 });
196 }
197
198 let mask = 1usize << target;
199 let mut prob_one = 0.0;
200
201 for i in 0..self.dim {
203 if (i & mask) != 0 {
204 prob_one += self.state[i].norm_sqr();
205 }
206 }
207
208 Ok(prob_one)
209 }
210
211 pub fn probability_zero(&self, target: QubitIndex) -> Result<f64> {
223 Ok(1.0 - self.probability_one(target)?)
224 }
225
226 pub fn measure(&mut self, target: QubitIndex) -> Result<bool> {
238 if target >= self.num_qubits {
239 return Err(SimulatorError::InvalidQubitIndex {
240 index: target,
241 num_qubits: self.num_qubits,
242 });
243 }
244
245 let prob_one = self.probability_one(target)?;
247
248 use scirs2_core::random::prelude::*;
250 let mut rng = thread_rng();
251 let random_value: f64 = rng.gen();
252
253 let outcome = random_value < prob_one;
254
255 self.collapse_to(target, outcome)?;
257
258 Ok(outcome)
259 }
260
261 fn collapse_to(&mut self, target: QubitIndex, outcome: bool) -> Result<()> {
268 let mask = 1usize << target;
269 let mut norm_sqr = 0.0;
270
271 for i in 0..self.dim {
274 let qubit_value = (i & mask) != 0;
275 if qubit_value != outcome {
276 self.state[i] = Complex64::new(0.0, 0.0);
277 } else {
278 norm_sqr += self.state[i].norm_sqr();
279 }
280 }
281
282 if norm_sqr < 1e-15 {
284 return Err(SimulatorError::InvalidState(
285 "Cannot collapse to zero-probability outcome".to_string(),
286 ));
287 }
288
289 let norm = norm_sqr.sqrt();
290 let scale = 1.0 / norm;
291 for i in 0..self.dim {
292 if ((i & mask) != 0) == outcome {
293 self.state[i] *= scale;
294 }
295 }
296
297 Ok(())
298 }
299
300 pub fn sample(&self, shots: usize) -> Result<Vec<Vec<bool>>> {
310 if shots == 0 {
311 return Ok(Vec::new());
312 }
313
314 use scirs2_core::random::prelude::*;
315 let mut rng = thread_rng();
316 let mut results = Vec::with_capacity(shots);
317
318 let mut cumulative_probs = Vec::with_capacity(self.dim);
320 let mut cumsum = 0.0;
321 for i in 0..self.dim {
322 cumsum += self.state[i].norm_sqr();
323 cumulative_probs.push(cumsum);
324 }
325
326 for _ in 0..shots {
328 let random_value: f64 = rng.gen();
329
330 let outcome_index = cumulative_probs
332 .binary_search_by(|&prob| {
333 if prob < random_value {
334 std::cmp::Ordering::Less
335 } else {
336 std::cmp::Ordering::Greater
337 }
338 })
339 .unwrap_or_else(|x| x);
340
341 let mut bitstring = Vec::with_capacity(self.num_qubits);
343 for q in 0..self.num_qubits {
344 bitstring.push((outcome_index & (1 << q)) != 0);
345 }
346 results.push(bitstring);
347 }
348
349 Ok(results)
350 }
351
352 pub fn get_counts(&self, shots: usize) -> Result<std::collections::HashMap<Vec<bool>, usize>> {
362 use std::collections::HashMap;
363
364 let samples = self.sample(shots)?;
365 let mut counts = HashMap::new();
366
367 for bitstring in samples {
368 *counts.entry(bitstring).or_insert(0) += 1;
369 }
370
371 Ok(counts)
372 }
373
374 pub fn sample_qubits(&self, qubits: &[QubitIndex], shots: usize) -> Result<Vec<Vec<bool>>> {
385 for &q in qubits {
387 if q >= self.num_qubits {
388 return Err(SimulatorError::InvalidQubitIndex {
389 index: q,
390 num_qubits: self.num_qubits,
391 });
392 }
393 }
394
395 let full_samples = self.sample(shots)?;
397
398 let results: Vec<Vec<bool>> = full_samples
400 .into_iter()
401 .map(|bitstring| qubits.iter().map(|&q| bitstring[q]).collect())
402 .collect();
403
404 Ok(results)
405 }
406}
407
408pub mod gates {
410 use super::*;
411
412 pub fn hadamard(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
424 if target >= state.num_qubits {
425 return Err(SimulatorError::InvalidQubitIndex {
426 index: target,
427 num_qubits: state.num_qubits,
428 });
429 }
430
431 let dim = state.dim;
432 let loop_dim = dim / 2;
433 let mask = 1usize << target;
434 let sqrt2_inv = Complex64::new(1.0 / 2.0f64.sqrt(), 0.0);
435
436 let state_data = state.amplitudes_mut();
437
438 if target == 0 {
439 for basis_idx in (0..dim).step_by(2) {
442 let temp0 = state_data[basis_idx];
443 let temp1 = state_data[basis_idx + 1];
444 state_data[basis_idx] = (temp0 + temp1) * sqrt2_inv;
445 state_data[basis_idx + 1] = (temp0 - temp1) * sqrt2_inv;
446 }
447 } else {
448 let mask_low = mask - 1;
451 let mask_high = !mask_low;
452
453 for state_idx in (0..loop_dim).step_by(2) {
454 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
455 let basis_idx_1 = basis_idx_0 | mask;
456
457 let temp_a0 = state_data[basis_idx_0];
458 let temp_a1 = state_data[basis_idx_1];
459 let temp_b0 = state_data[basis_idx_0 + 1];
460 let temp_b1 = state_data[basis_idx_1 + 1];
461
462 state_data[basis_idx_0] = (temp_a0 + temp_a1) * sqrt2_inv;
463 state_data[basis_idx_1] = (temp_a0 - temp_a1) * sqrt2_inv;
464 state_data[basis_idx_0 + 1] = (temp_b0 + temp_b1) * sqrt2_inv;
465 state_data[basis_idx_1 + 1] = (temp_b0 - temp_b1) * sqrt2_inv;
466 }
467 }
468
469 Ok(())
470 }
471
472 pub fn pauli_x(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
479 if target >= state.num_qubits {
480 return Err(SimulatorError::InvalidQubitIndex {
481 index: target,
482 num_qubits: state.num_qubits,
483 });
484 }
485
486 let dim = state.dim;
487 let loop_dim = dim / 2;
488 let mask = 1usize << target;
489 let mask_low = mask - 1;
490 let mask_high = !mask_low;
491
492 let state_data = state.amplitudes_mut();
493
494 if target == 0 {
495 for basis_idx in (0..dim).step_by(2) {
497 state_data.swap(basis_idx, basis_idx + 1);
498 }
499 } else {
500 for state_idx in (0..loop_dim).step_by(2) {
501 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
502 let basis_idx_1 = basis_idx_0 | mask;
503
504 state_data.swap(basis_idx_0, basis_idx_1);
505 state_data.swap(basis_idx_0 + 1, basis_idx_1 + 1);
506 }
507 }
508
509 Ok(())
510 }
511
512 pub fn pauli_y(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
519 if target >= state.num_qubits {
520 return Err(SimulatorError::InvalidQubitIndex {
521 index: target,
522 num_qubits: state.num_qubits,
523 });
524 }
525
526 let dim = state.dim;
527 let loop_dim = dim / 2;
528 let mask = 1usize << target;
529 let mask_low = mask - 1;
530 let mask_high = !mask_low;
531
532 let state_data = state.amplitudes_mut();
533 let i = Complex64::new(0.0, 1.0);
534
535 if target == 0 {
536 for basis_idx in (0..dim).step_by(2) {
537 let temp0 = state_data[basis_idx];
538 let temp1 = state_data[basis_idx + 1];
539 state_data[basis_idx] = -i * temp1;
540 state_data[basis_idx + 1] = i * temp0;
541 }
542 } else {
543 for state_idx in (0..loop_dim).step_by(2) {
544 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
545 let basis_idx_1 = basis_idx_0 | mask;
546
547 let temp_a0 = state_data[basis_idx_0];
548 let temp_a1 = state_data[basis_idx_1];
549 let temp_b0 = state_data[basis_idx_0 + 1];
550 let temp_b1 = state_data[basis_idx_1 + 1];
551
552 state_data[basis_idx_0] = -i * temp_a1;
553 state_data[basis_idx_1] = i * temp_a0;
554 state_data[basis_idx_0 + 1] = -i * temp_b1;
555 state_data[basis_idx_1 + 1] = i * temp_b0;
556 }
557 }
558
559 Ok(())
560 }
561
562 pub fn pauli_z(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
569 if target >= state.num_qubits {
570 return Err(SimulatorError::InvalidQubitIndex {
571 index: target,
572 num_qubits: state.num_qubits,
573 });
574 }
575
576 let dim = state.dim;
577 let loop_dim = dim / 2;
578 let mask = 1usize << target;
579 let mask_low = mask - 1;
580 let mask_high = !mask_low;
581
582 let state_data = state.amplitudes_mut();
583
584 for state_idx in 0..loop_dim {
585 let basis_idx = (state_idx & mask_low) | ((state_idx & mask_high) << 1) | mask;
586 state_data[basis_idx] = -state_data[basis_idx];
587 }
588
589 Ok(())
590 }
591
592 pub fn cnot(
603 state: &mut QulacsStateVector,
604 control: QubitIndex,
605 target: QubitIndex,
606 ) -> Result<()> {
607 if control >= state.num_qubits {
608 return Err(SimulatorError::InvalidQubitIndex {
609 index: control,
610 num_qubits: state.num_qubits,
611 });
612 }
613 if target >= state.num_qubits {
614 return Err(SimulatorError::InvalidQubitIndex {
615 index: target,
616 num_qubits: state.num_qubits,
617 });
618 }
619 if control == target {
620 return Err(SimulatorError::InvalidOperation(
621 "Control and target qubits must be different".to_string(),
622 ));
623 }
624
625 let dim = state.dim;
626 let loop_dim = dim / 4;
627 let target_mask = 1usize << target;
628 let control_mask = 1usize << control;
629
630 let min_qubit = control.min(target);
631 let max_qubit = control.max(target);
632 let min_qubit_mask = 1usize << min_qubit;
633 let max_qubit_mask = 1usize << (max_qubit - 1);
634 let low_mask = min_qubit_mask - 1;
635 let mid_mask = (max_qubit_mask - 1) ^ low_mask;
636 let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
637
638 let state_data = state.amplitudes_mut();
639
640 if target == 0 {
641 for state_idx in 0..loop_dim {
643 let basis_idx =
644 ((state_idx & mid_mask) << 1) | ((state_idx & high_mask) << 2) | control_mask;
645 state_data.swap(basis_idx, basis_idx + 1);
646 }
647 } else if control == 0 {
648 for state_idx in 0..loop_dim {
650 let basis_idx_0 = (state_idx & low_mask)
651 | ((state_idx & mid_mask) << 1)
652 | ((state_idx & high_mask) << 2)
653 | control_mask;
654 let basis_idx_1 = basis_idx_0 | target_mask;
655 state_data.swap(basis_idx_0, basis_idx_1);
656 }
657 } else {
658 for state_idx in (0..loop_dim).step_by(2) {
660 let basis_idx_0 = (state_idx & low_mask)
661 | ((state_idx & mid_mask) << 1)
662 | ((state_idx & high_mask) << 2)
663 | control_mask;
664 let basis_idx_1 = basis_idx_0 | target_mask;
665
666 state_data.swap(basis_idx_0, basis_idx_1);
667 state_data.swap(basis_idx_0 + 1, basis_idx_1 + 1);
668 }
669 }
670
671 Ok(())
672 }
673
674 pub fn cz(
682 state: &mut QulacsStateVector,
683 control: QubitIndex,
684 target: QubitIndex,
685 ) -> Result<()> {
686 if control >= state.num_qubits {
687 return Err(SimulatorError::InvalidQubitIndex {
688 index: control,
689 num_qubits: state.num_qubits,
690 });
691 }
692 if target >= state.num_qubits {
693 return Err(SimulatorError::InvalidQubitIndex {
694 index: target,
695 num_qubits: state.num_qubits,
696 });
697 }
698 if control == target {
699 return Err(SimulatorError::InvalidOperation(
700 "Control and target qubits must be different".to_string(),
701 ));
702 }
703
704 let dim = state.dim;
705 let loop_dim = dim / 4;
706 let target_mask = 1usize << target;
707 let control_mask = 1usize << control;
708
709 let min_qubit = control.min(target);
710 let max_qubit = control.max(target);
711 let min_qubit_mask = 1usize << min_qubit;
712 let max_qubit_mask = 1usize << (max_qubit - 1);
713 let low_mask = min_qubit_mask - 1;
714 let mid_mask = (max_qubit_mask - 1) ^ low_mask;
715 let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
716
717 let state_data = state.amplitudes_mut();
718
719 for state_idx in 0..loop_dim {
720 let basis_idx = (state_idx & low_mask)
721 | ((state_idx & mid_mask) << 1)
722 | ((state_idx & high_mask) << 2)
723 | control_mask
724 | target_mask;
725 state_data[basis_idx] = -state_data[basis_idx];
726 }
727
728 Ok(())
729 }
730
731 pub fn swap(
739 state: &mut QulacsStateVector,
740 qubit1: QubitIndex,
741 qubit2: QubitIndex,
742 ) -> Result<()> {
743 if qubit1 >= state.num_qubits {
744 return Err(SimulatorError::InvalidQubitIndex {
745 index: qubit1,
746 num_qubits: state.num_qubits,
747 });
748 }
749 if qubit2 >= state.num_qubits {
750 return Err(SimulatorError::InvalidQubitIndex {
751 index: qubit2,
752 num_qubits: state.num_qubits,
753 });
754 }
755 if qubit1 == qubit2 {
756 return Ok(()); }
758
759 let dim = state.dim;
760 let loop_dim = dim / 4;
761 let mask1 = 1usize << qubit1;
762 let mask2 = 1usize << qubit2;
763
764 let min_qubit = qubit1.min(qubit2);
765 let max_qubit = qubit1.max(qubit2);
766 let min_qubit_mask = 1usize << min_qubit;
767 let max_qubit_mask = 1usize << (max_qubit - 1);
768 let low_mask = min_qubit_mask - 1;
769 let mid_mask = (max_qubit_mask - 1) ^ low_mask;
770 let high_mask = !max_qubit_mask.wrapping_add(max_qubit_mask - 1);
771
772 let state_data = state.amplitudes_mut();
773
774 for state_idx in 0..loop_dim {
775 let basis_idx_0 = (state_idx & low_mask)
776 | ((state_idx & mid_mask) << 1)
777 | ((state_idx & high_mask) << 2);
778 let basis_idx_1 = basis_idx_0 | mask1;
779 let basis_idx_2 = basis_idx_0 | mask2;
780
781 state_data.swap(basis_idx_1, basis_idx_2);
782 }
783
784 Ok(())
785 }
786
787 pub fn rx(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
803 if target >= state.num_qubits {
804 return Err(SimulatorError::InvalidQubitIndex {
805 index: target,
806 num_qubits: state.num_qubits,
807 });
808 }
809
810 let dim = state.dim;
811 let loop_dim = dim / 2;
812 let mask = 1usize << target;
813 let mask_low = mask - 1;
814 let mask_high = !mask_low;
815
816 let cos_half = (angle / 2.0).cos();
817 let sin_half = (angle / 2.0).sin();
818 let i_sin_half = Complex64::new(0.0, -sin_half);
819
820 let state_data = state.amplitudes_mut();
821
822 if target == 0 {
823 for basis_idx in (0..dim).step_by(2) {
825 let amp0 = state_data[basis_idx];
826 let amp1 = state_data[basis_idx + 1];
827
828 state_data[basis_idx] = amp0 * cos_half + amp1 * i_sin_half;
829 state_data[basis_idx + 1] = amp0 * i_sin_half + amp1 * cos_half;
830 }
831 } else {
832 for state_idx in (0..loop_dim).step_by(2) {
834 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
835 let basis_idx_1 = basis_idx_0 | mask;
836
837 let amp_a0 = state_data[basis_idx_0];
839 let amp_a1 = state_data[basis_idx_1];
840 let amp_b0 = state_data[basis_idx_0 + 1];
841 let amp_b1 = state_data[basis_idx_1 + 1];
842
843 state_data[basis_idx_0] = amp_a0 * cos_half + amp_a1 * i_sin_half;
844 state_data[basis_idx_1] = amp_a0 * i_sin_half + amp_a1 * cos_half;
845 state_data[basis_idx_0 + 1] = amp_b0 * cos_half + amp_b1 * i_sin_half;
846 state_data[basis_idx_1 + 1] = amp_b0 * i_sin_half + amp_b1 * cos_half;
847 }
848 }
849
850 Ok(())
851 }
852
853 pub fn ry(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
869 if target >= state.num_qubits {
870 return Err(SimulatorError::InvalidQubitIndex {
871 index: target,
872 num_qubits: state.num_qubits,
873 });
874 }
875
876 let dim = state.dim;
877 let loop_dim = dim / 2;
878 let mask = 1usize << target;
879 let mask_low = mask - 1;
880 let mask_high = !mask_low;
881
882 let cos_half = (angle / 2.0).cos();
883 let sin_half = (angle / 2.0).sin();
884
885 let state_data = state.amplitudes_mut();
886
887 if target == 0 {
888 for basis_idx in (0..dim).step_by(2) {
890 let amp0 = state_data[basis_idx];
891 let amp1 = state_data[basis_idx + 1];
892
893 state_data[basis_idx] = amp0 * cos_half - amp1 * sin_half;
894 state_data[basis_idx + 1] = amp0 * sin_half + amp1 * cos_half;
895 }
896 } else {
897 for state_idx in (0..loop_dim).step_by(2) {
899 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
900 let basis_idx_1 = basis_idx_0 | mask;
901
902 let amp_a0 = state_data[basis_idx_0];
903 let amp_a1 = state_data[basis_idx_1];
904 let amp_b0 = state_data[basis_idx_0 + 1];
905 let amp_b1 = state_data[basis_idx_1 + 1];
906
907 state_data[basis_idx_0] = amp_a0 * cos_half - amp_a1 * sin_half;
908 state_data[basis_idx_1] = amp_a0 * sin_half + amp_a1 * cos_half;
909 state_data[basis_idx_0 + 1] = amp_b0 * cos_half - amp_b1 * sin_half;
910 state_data[basis_idx_1 + 1] = amp_b0 * sin_half + amp_b1 * cos_half;
911 }
912 }
913
914 Ok(())
915 }
916
917 pub fn rz(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
933 if target >= state.num_qubits {
934 return Err(SimulatorError::InvalidQubitIndex {
935 index: target,
936 num_qubits: state.num_qubits,
937 });
938 }
939
940 let dim = state.dim;
941 let loop_dim = dim / 2;
942 let mask = 1usize << target;
943 let mask_low = mask - 1;
944 let mask_high = !mask_low;
945
946 let phase_0 = Complex64::from_polar(1.0, -angle / 2.0);
947 let phase_1 = Complex64::from_polar(1.0, angle / 2.0);
948
949 let state_data = state.amplitudes_mut();
950
951 for state_idx in 0..loop_dim {
952 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
953 let basis_idx_1 = basis_idx_0 | mask;
954
955 state_data[basis_idx_0] *= phase_0;
956 state_data[basis_idx_1] *= phase_1;
957 }
958
959 Ok(())
960 }
961
962 pub fn phase(state: &mut QulacsStateVector, target: QubitIndex, angle: f64) -> Result<()> {
972 if target >= state.num_qubits {
973 return Err(SimulatorError::InvalidQubitIndex {
974 index: target,
975 num_qubits: state.num_qubits,
976 });
977 }
978
979 let dim = state.dim;
980 let loop_dim = dim / 2;
981 let mask = 1usize << target;
982 let mask_low = mask - 1;
983 let mask_high = !mask_low;
984
985 let phase_factor = Complex64::from_polar(1.0, angle);
986
987 let state_data = state.amplitudes_mut();
988
989 for state_idx in 0..loop_dim {
990 let basis_idx = (state_idx & mask_low) | ((state_idx & mask_high) << 1) | mask;
991 state_data[basis_idx] *= phase_factor;
992 }
993
994 Ok(())
995 }
996
997 pub fn s(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
1006 phase(state, target, std::f64::consts::FRAC_PI_2)
1007 }
1008
1009 pub fn sdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
1018 phase(state, target, -std::f64::consts::FRAC_PI_2)
1019 }
1020
1021 pub fn t(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
1030 phase(state, target, std::f64::consts::FRAC_PI_4)
1031 }
1032
1033 pub fn tdg(state: &mut QulacsStateVector, target: QubitIndex) -> Result<()> {
1042 phase(state, target, -std::f64::consts::FRAC_PI_4)
1043 }
1044
1045 pub fn u3(
1063 state: &mut QulacsStateVector,
1064 target: QubitIndex,
1065 theta: f64,
1066 phi: f64,
1067 lambda: f64,
1068 ) -> Result<()> {
1069 if target >= state.num_qubits {
1070 return Err(SimulatorError::InvalidQubitIndex {
1071 index: target,
1072 num_qubits: state.num_qubits,
1073 });
1074 }
1075
1076 let dim = state.dim;
1077 let loop_dim = dim / 2;
1078 let mask = 1usize << target;
1079 let mask_low = mask - 1;
1080 let mask_high = !mask_low;
1081
1082 let cos_half = (theta / 2.0).cos();
1083 let sin_half = (theta / 2.0).sin();
1084
1085 let u00 = Complex64::new(cos_half, 0.0);
1087 let u01 = -Complex64::from_polar(sin_half, lambda);
1088 let u10 = Complex64::from_polar(sin_half, phi);
1089 let u11 = Complex64::from_polar(cos_half, phi + lambda);
1090
1091 let state_data = state.amplitudes_mut();
1092
1093 if target == 0 {
1094 for basis_idx in (0..dim).step_by(2) {
1096 let amp0 = state_data[basis_idx];
1097 let amp1 = state_data[basis_idx + 1];
1098
1099 state_data[basis_idx] = u00 * amp0 + u01 * amp1;
1100 state_data[basis_idx + 1] = u10 * amp0 + u11 * amp1;
1101 }
1102 } else {
1103 for state_idx in (0..loop_dim).step_by(2) {
1105 let basis_idx_0 = (state_idx & mask_low) | ((state_idx & mask_high) << 1);
1106 let basis_idx_1 = basis_idx_0 | mask;
1107
1108 let amp_a0 = state_data[basis_idx_0];
1109 let amp_a1 = state_data[basis_idx_1];
1110 let amp_b0 = state_data[basis_idx_0 + 1];
1111 let amp_b1 = state_data[basis_idx_1 + 1];
1112
1113 state_data[basis_idx_0] = u00 * amp_a0 + u01 * amp_a1;
1114 state_data[basis_idx_1] = u10 * amp_a0 + u11 * amp_a1;
1115 state_data[basis_idx_0 + 1] = u00 * amp_b0 + u01 * amp_b1;
1116 state_data[basis_idx_1 + 1] = u10 * amp_b0 + u11 * amp_b1;
1117 }
1118 }
1119
1120 Ok(())
1121 }
1122
1123 pub fn toffoli(
1134 state: &mut QulacsStateVector,
1135 control1: QubitIndex,
1136 control2: QubitIndex,
1137 target: QubitIndex,
1138 ) -> Result<()> {
1139 if control1 >= state.num_qubits {
1140 return Err(SimulatorError::InvalidQubitIndex {
1141 index: control1,
1142 num_qubits: state.num_qubits,
1143 });
1144 }
1145 if control2 >= state.num_qubits {
1146 return Err(SimulatorError::InvalidQubitIndex {
1147 index: control2,
1148 num_qubits: state.num_qubits,
1149 });
1150 }
1151 if target >= state.num_qubits {
1152 return Err(SimulatorError::InvalidQubitIndex {
1153 index: target,
1154 num_qubits: state.num_qubits,
1155 });
1156 }
1157 if control1 == control2 || control1 == target || control2 == target {
1158 return Err(SimulatorError::InvalidOperation(
1159 "Control and target qubits must be different".to_string(),
1160 ));
1161 }
1162
1163 let dim = state.dim;
1164 let loop_dim = dim / 8;
1165 let num_qubits = state.num_qubits;
1166 let control1_mask = 1usize << control1;
1167 let control2_mask = 1usize << control2;
1168 let target_mask = 1usize << target;
1169
1170 let state_data = state.amplitudes_mut();
1171
1172 for i in 0..loop_dim {
1174 let mut basis_idx = 0;
1176 let mut temp = i;
1177
1178 for bit_pos in 0..num_qubits {
1180 if bit_pos != control1 && bit_pos != control2 && bit_pos != target {
1181 basis_idx |= (temp & 1) << bit_pos;
1182 temp >>= 1;
1183 }
1184 }
1185
1186 basis_idx |= control1_mask | control2_mask;
1188
1189 let idx_0 = basis_idx & !target_mask; let idx_1 = basis_idx | target_mask; state_data.swap(idx_0, idx_1);
1194 }
1195
1196 Ok(())
1197 }
1198
1199 pub fn fredkin(
1210 state: &mut QulacsStateVector,
1211 control: QubitIndex,
1212 target1: QubitIndex,
1213 target2: QubitIndex,
1214 ) -> Result<()> {
1215 if control >= state.num_qubits {
1216 return Err(SimulatorError::InvalidQubitIndex {
1217 index: control,
1218 num_qubits: state.num_qubits,
1219 });
1220 }
1221 if target1 >= state.num_qubits {
1222 return Err(SimulatorError::InvalidQubitIndex {
1223 index: target1,
1224 num_qubits: state.num_qubits,
1225 });
1226 }
1227 if target2 >= state.num_qubits {
1228 return Err(SimulatorError::InvalidQubitIndex {
1229 index: target2,
1230 num_qubits: state.num_qubits,
1231 });
1232 }
1233 if control == target1 || control == target2 || target1 == target2 {
1234 return Err(SimulatorError::InvalidOperation(
1235 "Control and target qubits must be different".to_string(),
1236 ));
1237 }
1238
1239 let dim = state.dim;
1240 let loop_dim = dim / 8;
1241 let num_qubits = state.num_qubits;
1242 let control_mask = 1usize << control;
1243 let target1_mask = 1usize << target1;
1244 let target2_mask = 1usize << target2;
1245
1246 let state_data = state.amplitudes_mut();
1247
1248 for i in 0..loop_dim {
1250 let mut basis_idx = 0;
1252 let mut temp = i;
1253
1254 for bit_pos in 0..num_qubits {
1256 if bit_pos != control && bit_pos != target1 && bit_pos != target2 {
1257 basis_idx |= (temp & 1) << bit_pos;
1258 temp >>= 1;
1259 }
1260 }
1261
1262 basis_idx |= control_mask;
1264
1265 let idx_01 = basis_idx | target2_mask; let idx_10 = basis_idx | target1_mask; state_data.swap(idx_01, idx_10);
1270 }
1271
1272 Ok(())
1273 }
1274}
1275
1276pub mod observable {
1280 use super::*;
1281 use scirs2_core::ndarray::Array2;
1282 use scirs2_core::Complex64;
1283 use std::collections::HashMap;
1284
1285 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1287 pub enum PauliOperator {
1288 I,
1290 X,
1292 Y,
1294 Z,
1296 }
1297
1298 impl PauliOperator {
1299 pub fn matrix(&self) -> Array2<Complex64> {
1301 match self {
1302 PauliOperator::I => {
1303 let mut mat = Array2::zeros((2, 2));
1304 mat[[0, 0]] = Complex64::new(1.0, 0.0);
1305 mat[[1, 1]] = Complex64::new(1.0, 0.0);
1306 mat
1307 }
1308 PauliOperator::X => {
1309 let mut mat = Array2::zeros((2, 2));
1310 mat[[0, 1]] = Complex64::new(1.0, 0.0);
1311 mat[[1, 0]] = Complex64::new(1.0, 0.0);
1312 mat
1313 }
1314 PauliOperator::Y => {
1315 let mut mat = Array2::zeros((2, 2));
1316 mat[[0, 1]] = Complex64::new(0.0, -1.0);
1317 mat[[1, 0]] = Complex64::new(0.0, 1.0);
1318 mat
1319 }
1320 PauliOperator::Z => {
1321 let mut mat = Array2::zeros((2, 2));
1322 mat[[0, 0]] = Complex64::new(1.0, 0.0);
1323 mat[[1, 1]] = Complex64::new(-1.0, 0.0);
1324 mat
1325 }
1326 }
1327 }
1328
1329 pub fn eigenvalue(&self, basis_state: bool) -> f64 {
1331 match self {
1332 PauliOperator::I => 1.0,
1333 PauliOperator::X => 0.0, PauliOperator::Y => 0.0, PauliOperator::Z => {
1336 if basis_state {
1337 -1.0
1338 } else {
1339 1.0
1340 }
1341 }
1342 }
1343 }
1344
1345 pub fn commutes_with_z(&self) -> bool {
1347 matches!(self, PauliOperator::I | PauliOperator::Z)
1348 }
1349 }
1350
1351 #[derive(Debug, Clone)]
1353 pub struct PauliObservable {
1354 pub operators: HashMap<usize, PauliOperator>,
1356 pub coefficient: f64,
1358 }
1359
1360 impl PauliObservable {
1361 pub fn new(operators: HashMap<usize, PauliOperator>, coefficient: f64) -> Self {
1363 Self {
1364 operators,
1365 coefficient,
1366 }
1367 }
1368
1369 pub fn identity(num_qubits: usize) -> Self {
1371 let mut operators = HashMap::new();
1372 for i in 0..num_qubits {
1373 operators.insert(i, PauliOperator::I);
1374 }
1375 Self {
1376 operators,
1377 coefficient: 1.0,
1378 }
1379 }
1380
1381 pub fn pauli_z(qubits: &[usize]) -> Self {
1383 let mut operators = HashMap::new();
1384 for &qubit in qubits {
1385 operators.insert(qubit, PauliOperator::Z);
1386 }
1387 Self {
1388 operators,
1389 coefficient: 1.0,
1390 }
1391 }
1392
1393 pub fn pauli_x(qubits: &[usize]) -> Self {
1395 let mut operators = HashMap::new();
1396 for &qubit in qubits {
1397 operators.insert(qubit, PauliOperator::X);
1398 }
1399 Self {
1400 operators,
1401 coefficient: 1.0,
1402 }
1403 }
1404
1405 pub fn pauli_y(qubits: &[usize]) -> Self {
1407 let mut operators = HashMap::new();
1408 for &qubit in qubits {
1409 operators.insert(qubit, PauliOperator::Y);
1410 }
1411 Self {
1412 operators,
1413 coefficient: 1.0,
1414 }
1415 }
1416
1417 pub fn expectation_value(&self, state: &QulacsStateVector) -> f64 {
1419 let mut result = 0.0;
1420
1421 for i in 0..state.dim() {
1424 let prob = state.amplitudes()[i].norm_sqr();
1425 if prob < 1e-15 {
1426 continue;
1427 }
1428
1429 let mut eigenvalue = 1.0;
1431 for (&qubit, &op) in &self.operators {
1432 let bit = ((i >> qubit) & 1) == 1;
1433 match op {
1434 PauliOperator::I => {}
1435 PauliOperator::Z => {
1436 eigenvalue *= if bit { -1.0 } else { 1.0 };
1437 }
1438 PauliOperator::X | PauliOperator::Y => {
1439 return 0.0;
1442 }
1443 }
1444 }
1445
1446 result += prob * eigenvalue;
1447 }
1448
1449 result * self.coefficient
1450 }
1451
1452 pub fn with_coefficient(mut self, coefficient: f64) -> Self {
1454 self.coefficient = coefficient;
1455 self
1456 }
1457
1458 pub fn weight(&self) -> usize {
1460 self.operators
1461 .values()
1462 .filter(|&&op| op != PauliOperator::I)
1463 .count()
1464 }
1465 }
1466
1467 #[derive(Debug, Clone)]
1469 pub struct HermitianObservable {
1470 pub matrix: Array2<Complex64>,
1472 pub num_qubits: usize,
1474 }
1475
1476 impl HermitianObservable {
1477 pub fn new(matrix: Array2<Complex64>) -> Result<Self> {
1479 let (n, m) = (matrix.nrows(), matrix.ncols());
1480 if n != m {
1481 return Err(SimulatorError::InvalidObservable(
1482 "Matrix must be square".to_string(),
1483 ));
1484 }
1485
1486 if n == 0 || (n & (n - 1)) != 0 {
1487 return Err(SimulatorError::InvalidObservable(
1488 "Dimension must be a power of 2".to_string(),
1489 ));
1490 }
1491
1492 let num_qubits = n.trailing_zeros() as usize;
1493
1494 Ok(Self { matrix, num_qubits })
1495 }
1496
1497 pub fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
1499 if state.num_qubits() != self.num_qubits {
1500 return Err(SimulatorError::InvalidObservable(
1501 "Observable dimension doesn't match state".to_string(),
1502 ));
1503 }
1504
1505 let psi = state.amplitudes();
1506 let mut result = Complex64::new(0.0, 0.0);
1507
1508 for i in 0..state.dim() {
1509 for j in 0..state.dim() {
1510 result += psi[i].conj() * self.matrix[[i, j]] * psi[j];
1511 }
1512 }
1513
1514 Ok(result.re)
1515 }
1516 }
1517
1518 #[derive(Debug, Clone)]
1520 pub struct CompositeObservable {
1521 pub terms: Vec<PauliObservable>,
1523 }
1524
1525 impl CompositeObservable {
1526 pub fn new() -> Self {
1528 Self { terms: Vec::new() }
1529 }
1530
1531 pub fn add_term(mut self, observable: PauliObservable) -> Self {
1533 self.terms.push(observable);
1534 self
1535 }
1536
1537 pub fn expectation_value(&self, state: &QulacsStateVector) -> f64 {
1539 self.terms
1540 .iter()
1541 .map(|term| term.expectation_value(state))
1542 .sum()
1543 }
1544
1545 pub fn num_terms(&self) -> usize {
1547 self.terms.len()
1548 }
1549 }
1550
1551 impl Default for CompositeObservable {
1552 fn default() -> Self {
1553 Self::new()
1554 }
1555 }
1556}
1557
1558pub mod circuit_api {
1567 use super::*;
1568 use std::collections::HashMap;
1569
1570 #[derive(Clone)]
1584 pub struct QulacsCircuit {
1585 num_qubits: usize,
1587 state: QulacsStateVector,
1589 gates: Vec<GateRecord>,
1591 measurements: HashMap<usize, Vec<bool>>,
1593 noise_model: Option<crate::noise_models::NoiseModel>,
1595 }
1596
1597 #[derive(Debug, Clone)]
1599 pub struct GateRecord {
1600 pub name: String,
1601 pub qubits: Vec<usize>,
1602 pub params: Vec<f64>,
1603 }
1604
1605 impl QulacsCircuit {
1606 pub fn new(num_qubits: usize) -> Result<Self> {
1608 Ok(Self {
1609 num_qubits,
1610 state: QulacsStateVector::new(num_qubits)?,
1611 gates: Vec::new(),
1612 measurements: HashMap::new(),
1613 noise_model: None,
1614 })
1615 }
1616
1617 pub fn num_qubits(&self) -> usize {
1619 self.num_qubits
1620 }
1621
1622 pub fn state(&self) -> &QulacsStateVector {
1624 &self.state
1625 }
1626
1627 pub fn gates(&self) -> &[GateRecord] {
1629 &self.gates
1630 }
1631
1632 pub fn reset(&mut self) -> Result<()> {
1634 self.state = QulacsStateVector::new(self.num_qubits)?;
1635 self.gates.clear();
1636 self.measurements.clear();
1637 Ok(())
1638 }
1639
1640 pub fn h(&mut self, qubit: usize) -> &mut Self {
1644 super::gates::hadamard(&mut self.state, qubit).ok();
1645 self.gates.push(GateRecord {
1646 name: "H".to_string(),
1647 qubits: vec![qubit],
1648 params: vec![],
1649 });
1650 self
1651 }
1652
1653 pub fn x(&mut self, qubit: usize) -> &mut Self {
1655 super::gates::pauli_x(&mut self.state, qubit).ok();
1656 self.gates.push(GateRecord {
1657 name: "X".to_string(),
1658 qubits: vec![qubit],
1659 params: vec![],
1660 });
1661 self
1662 }
1663
1664 pub fn y(&mut self, qubit: usize) -> &mut Self {
1666 super::gates::pauli_y(&mut self.state, qubit).ok();
1667 self.gates.push(GateRecord {
1668 name: "Y".to_string(),
1669 qubits: vec![qubit],
1670 params: vec![],
1671 });
1672 self
1673 }
1674
1675 pub fn z(&mut self, qubit: usize) -> &mut Self {
1677 super::gates::pauli_z(&mut self.state, qubit).ok();
1678 self.gates.push(GateRecord {
1679 name: "Z".to_string(),
1680 qubits: vec![qubit],
1681 params: vec![],
1682 });
1683 self
1684 }
1685
1686 pub fn s(&mut self, qubit: usize) -> &mut Self {
1688 super::gates::s(&mut self.state, qubit).ok();
1689 self.gates.push(GateRecord {
1690 name: "S".to_string(),
1691 qubits: vec![qubit],
1692 params: vec![],
1693 });
1694 self
1695 }
1696
1697 pub fn sdg(&mut self, qubit: usize) -> &mut Self {
1699 super::gates::sdg(&mut self.state, qubit).ok();
1700 self.gates.push(GateRecord {
1701 name: "S†".to_string(),
1702 qubits: vec![qubit],
1703 params: vec![],
1704 });
1705 self
1706 }
1707
1708 pub fn t(&mut self, qubit: usize) -> &mut Self {
1710 super::gates::t(&mut self.state, qubit).ok();
1711 self.gates.push(GateRecord {
1712 name: "T".to_string(),
1713 qubits: vec![qubit],
1714 params: vec![],
1715 });
1716 self
1717 }
1718
1719 pub fn tdg(&mut self, qubit: usize) -> &mut Self {
1721 super::gates::tdg(&mut self.state, qubit).ok();
1722 self.gates.push(GateRecord {
1723 name: "T†".to_string(),
1724 qubits: vec![qubit],
1725 params: vec![],
1726 });
1727 self
1728 }
1729
1730 pub fn rx(&mut self, qubit: usize, angle: f64) -> &mut Self {
1732 super::gates::rx(&mut self.state, qubit, angle).ok();
1733 self.gates.push(GateRecord {
1734 name: "RX".to_string(),
1735 qubits: vec![qubit],
1736 params: vec![angle],
1737 });
1738 self
1739 }
1740
1741 pub fn ry(&mut self, qubit: usize, angle: f64) -> &mut Self {
1743 super::gates::ry(&mut self.state, qubit, angle).ok();
1744 self.gates.push(GateRecord {
1745 name: "RY".to_string(),
1746 qubits: vec![qubit],
1747 params: vec![angle],
1748 });
1749 self
1750 }
1751
1752 pub fn rz(&mut self, qubit: usize, angle: f64) -> &mut Self {
1754 super::gates::rz(&mut self.state, qubit, angle).ok();
1755 self.gates.push(GateRecord {
1756 name: "RZ".to_string(),
1757 qubits: vec![qubit],
1758 params: vec![angle],
1759 });
1760 self
1761 }
1762
1763 pub fn phase(&mut self, qubit: usize, angle: f64) -> &mut Self {
1765 super::gates::phase(&mut self.state, qubit, angle).ok();
1766 self.gates.push(GateRecord {
1767 name: "Phase".to_string(),
1768 qubits: vec![qubit],
1769 params: vec![angle],
1770 });
1771 self
1772 }
1773
1774 pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self {
1778 super::gates::cnot(&mut self.state, control, target).ok();
1779 self.gates.push(GateRecord {
1780 name: "CNOT".to_string(),
1781 qubits: vec![control, target],
1782 params: vec![],
1783 });
1784 self
1785 }
1786
1787 pub fn cz(&mut self, control: usize, target: usize) -> &mut Self {
1789 super::gates::cz(&mut self.state, control, target).ok();
1790 self.gates.push(GateRecord {
1791 name: "CZ".to_string(),
1792 qubits: vec![control, target],
1793 params: vec![],
1794 });
1795 self
1796 }
1797
1798 pub fn swap(&mut self, qubit1: usize, qubit2: usize) -> &mut Self {
1800 super::gates::swap(&mut self.state, qubit1, qubit2).ok();
1801 self.gates.push(GateRecord {
1802 name: "SWAP".to_string(),
1803 qubits: vec![qubit1, qubit2],
1804 params: vec![],
1805 });
1806 self
1807 }
1808
1809 pub fn measure(&mut self, qubit: usize) -> Result<bool> {
1813 let outcome = self.state.measure(qubit)?;
1814 self.measurements.entry(qubit).or_default().push(outcome);
1815 Ok(outcome)
1816 }
1817
1818 pub fn measure_all(&mut self) -> Result<Vec<bool>> {
1820 (0..self.num_qubits).map(|q| self.measure(q)).collect()
1821 }
1822
1823 pub fn run(&mut self, shots: usize) -> Result<HashMap<String, usize>> {
1825 let mut counts = HashMap::new();
1826
1827 for _ in 0..shots {
1828 let saved_state = self.state.clone();
1830
1831 let outcomes = self.measure_all()?;
1833
1834 let bitstring: String = outcomes
1836 .iter()
1837 .map(|&b| if b { '1' } else { '0' })
1838 .collect();
1839
1840 *counts.entry(bitstring).or_insert(0) += 1;
1842
1843 self.state = saved_state;
1845 }
1846
1847 Ok(counts)
1848 }
1849
1850 pub fn get_measurements(&self, qubit: usize) -> Option<&Vec<bool>> {
1852 self.measurements.get(&qubit)
1853 }
1854
1855 pub fn bell_pair(&mut self, qubit0: usize, qubit1: usize) -> &mut Self {
1859 self.h(qubit0);
1860 self.cnot(qubit0, qubit1);
1861 self
1862 }
1863
1864 pub fn qft(&mut self, qubits: &[usize]) -> &mut Self {
1866 let n = qubits.len();
1867 for i in 0..n {
1868 let q = qubits[i];
1869 self.h(q);
1870 for j in (i + 1)..n {
1871 let control = qubits[j];
1872 let angle = std::f64::consts::PI / (1 << (j - i)) as f64;
1873 self.controlled_phase(control, q, angle);
1874 }
1875 }
1876 for i in 0..(n / 2) {
1878 self.swap(qubits[i], qubits[n - 1 - i]);
1879 }
1880 self
1881 }
1882
1883 pub fn controlled_phase(&mut self, control: usize, target: usize, angle: f64) -> &mut Self {
1886 self.rz(target, angle / 2.0);
1888 self.cnot(control, target);
1889 self.rz(target, -angle / 2.0);
1890 self.cnot(control, target);
1891
1892 self.gates.push(GateRecord {
1894 name: "CPhase".to_string(),
1895 qubits: vec![control, target],
1896 params: vec![angle],
1897 });
1898 self
1899 }
1900
1901 pub fn probabilities(&self) -> Vec<f64> {
1905 self.state
1906 .amplitudes()
1907 .iter()
1908 .map(|amp| amp.norm_sqr())
1909 .collect()
1910 }
1911
1912 pub fn expectation<O: Observable>(&self, observable: &O) -> Result<f64> {
1914 observable.expectation_value(&self.state)
1915 }
1916
1917 pub fn depth(&self) -> usize {
1919 self.gates.len()
1922 }
1923
1924 pub fn gate_count(&self) -> usize {
1926 self.gates.len()
1927 }
1928
1929 pub fn set_noise_model(&mut self, noise_model: crate::noise_models::NoiseModel) {
1950 self.noise_model = Some(noise_model);
1951 }
1952
1953 pub fn clear_noise_model(&mut self) {
1955 self.noise_model = None;
1956 }
1957
1958 pub fn has_noise_model(&self) -> bool {
1960 self.noise_model.is_some()
1961 }
1962
1963 fn apply_noise_to_qubit(&mut self, qubit: usize) -> Result<()> {
1967 if let Some(ref noise_model) = self.noise_model {
1968 let num_states = 2_usize.pow(self.num_qubits as u32);
1970 let mut noisy_amplitudes = self.state.amplitudes().to_vec();
1971
1972 for idx in 0..num_states {
1974 let qubit_state = (idx >> qubit) & 1;
1976
1977 let local_state = if qubit_state == 0 {
1979 Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)])
1980 } else {
1981 Array1::from_vec(vec![Complex64::new(0.0, 0.0), Complex64::new(1.0, 0.0)])
1982 };
1983
1984 let _noisy_local = noise_model.apply_single_qubit(&local_state, qubit)?;
1986
1987 }
1991
1992 self.state =
1994 QulacsStateVector::from_amplitudes(Array1::from_vec(noisy_amplitudes))?;
1995 }
1996 Ok(())
1997 }
1998
1999 pub fn run_with_noise(&mut self, shots: usize) -> Result<HashMap<String, usize>> {
2003 if self.noise_model.is_none() {
2004 return self.run(shots);
2005 }
2006
2007 let mut counts: HashMap<String, usize> = HashMap::new();
2008
2009 for _ in 0..shots {
2010 let initial_state = self.state.clone();
2012
2013 let measurement = self.measure_all()?;
2017 let bitstring: String = measurement
2018 .iter()
2019 .map(|&b| if b { '1' } else { '0' })
2020 .collect();
2021
2022 *counts.entry(bitstring).or_insert(0) += 1;
2023
2024 self.state = initial_state;
2026 }
2027
2028 Ok(counts)
2029 }
2030 }
2031
2032 pub trait Observable {
2034 fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64>;
2035 }
2036
2037 impl Observable for super::observable::PauliObservable {
2039 fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
2040 Ok(super::observable::PauliObservable::expectation_value(
2041 self, state,
2042 ))
2043 }
2044 }
2045
2046 impl Observable for super::observable::HermitianObservable {
2048 fn expectation_value(&self, state: &QulacsStateVector) -> Result<f64> {
2049 super::observable::HermitianObservable::expectation_value(self, state)
2050 }
2051 }
2052}
2053
2054#[cfg(test)]
2055mod tests {
2056 use super::gates;
2057 use super::*;
2058 use scirs2_core::Float;
2059
2060 #[test]
2061 fn test_state_creation() {
2062 let state = QulacsStateVector::new(2).unwrap();
2063 assert_eq!(state.num_qubits(), 2);
2064 assert_eq!(state.dim(), 4);
2065 assert!((state.norm_squared() - 1.0).abs() < 1e-10);
2066 }
2067
2068 #[test]
2069 fn test_hadamard_gate() {
2070 let mut state = QulacsStateVector::new(1).unwrap();
2071 gates::hadamard(&mut state, 0).unwrap();
2072
2073 let expected_amp = 1.0 / 2.0f64.sqrt();
2074 assert!((state.amplitudes()[0].re - expected_amp).abs() < 1e-10);
2075 assert!((state.amplitudes()[1].re - expected_amp).abs() < 1e-10);
2076 }
2077
2078 #[test]
2079 fn test_pauli_x_gate() {
2080 let mut state = QulacsStateVector::new(1).unwrap();
2081 gates::pauli_x(&mut state, 0).unwrap();
2082
2083 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2084 assert!((state.amplitudes()[1].norm() - 1.0).abs() < 1e-10);
2085 }
2086
2087 #[test]
2088 fn test_cnot_gate() {
2089 let mut state = QulacsStateVector::new(2).unwrap();
2090
2091 gates::pauli_x(&mut state, 0).unwrap();
2093 gates::pauli_x(&mut state, 1).unwrap();
2094
2095 assert!((state.amplitudes()[3].norm() - 1.0).abs() < 1e-10);
2097
2098 gates::cnot(&mut state, 0, 1).unwrap();
2103
2104 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2107 assert!((state.amplitudes()[1].norm() - 1.0).abs() < 1e-10);
2108 assert!((state.amplitudes()[2].norm() - 0.0).abs() < 1e-10);
2109 assert!((state.amplitudes()[3].norm() - 0.0).abs() < 1e-10);
2110 }
2111
2112 #[test]
2113 fn test_bell_state() {
2114 let mut state = QulacsStateVector::new(2).unwrap();
2115
2116 gates::hadamard(&mut state, 0).unwrap();
2118 gates::cnot(&mut state, 0, 1).unwrap();
2119
2120 let expected_amp = 1.0 / 2.0f64.sqrt();
2121 assert!((state.amplitudes()[0].re - expected_amp).abs() < 1e-10);
2122 assert!((state.amplitudes()[1].norm() - 0.0).abs() < 1e-10);
2123 assert!((state.amplitudes()[2].norm() - 0.0).abs() < 1e-10);
2124 assert!((state.amplitudes()[3].re - expected_amp).abs() < 1e-10);
2125 }
2126
2127 #[test]
2128 fn test_norm_squared() {
2129 let state = QulacsStateVector::new(3).unwrap();
2130 assert!((state.norm_squared() - 1.0).abs() < 1e-10);
2131 }
2132
2133 #[test]
2134 fn test_inner_product() {
2135 let state1 = QulacsStateVector::new(2).unwrap();
2136 let state2 = QulacsStateVector::new(2).unwrap();
2137
2138 let inner = state1.inner_product(&state2).unwrap();
2139 assert!((inner.re - 1.0).abs() < 1e-10);
2140 assert!(inner.im.abs() < 1e-10);
2141 }
2142
2143 #[test]
2146 fn test_rx_gate() {
2147 use std::f64::consts::PI;
2148
2149 let mut state1 = QulacsStateVector::new(1).unwrap();
2151 gates::rx(&mut state1, 0, PI).unwrap();
2152
2153 let mut state2 = QulacsStateVector::new(1).unwrap();
2154 gates::pauli_x(&mut state2, 0).unwrap();
2155
2156 assert!(state1.amplitudes()[0].norm() < 1e-10);
2159 assert!((state1.amplitudes()[1].norm() - 1.0).abs() < 1e-10);
2160
2161 let mut state3 = QulacsStateVector::new(1).unwrap();
2163 gates::rx(&mut state3, 0, PI / 2.0).unwrap();
2164
2165 let expected = 1.0 / 2.0f64.sqrt();
2166 assert!((state3.amplitudes()[0].re - expected).abs() < 1e-10);
2167 assert!(state3.amplitudes()[0].im.abs() < 1e-10);
2168 assert!(state3.amplitudes()[1].re.abs() < 1e-10);
2169 assert!((state3.amplitudes()[1].im + expected).abs() < 1e-10); }
2171
2172 #[test]
2173 fn test_ry_gate() {
2174 use std::f64::consts::PI;
2175
2176 let mut state = QulacsStateVector::new(1).unwrap();
2178 gates::ry(&mut state, 0, PI / 2.0).unwrap();
2179
2180 let expected = 1.0 / 2.0f64.sqrt();
2181 assert!((state.amplitudes()[0].re - expected).abs() < 1e-10);
2182 assert!((state.amplitudes()[1].re - expected).abs() < 1e-10);
2183 assert!(state.amplitudes()[0].im.abs() < 1e-10);
2184 assert!(state.amplitudes()[1].im.abs() < 1e-10);
2185
2186 let mut bell_state = QulacsStateVector::new(2).unwrap();
2188 gates::ry(&mut bell_state, 0, PI / 2.0).unwrap();
2189 gates::cnot(&mut bell_state, 0, 1).unwrap();
2190
2191 assert!((bell_state.amplitudes()[0].norm() - expected).abs() < 1e-10);
2193 assert!((bell_state.amplitudes()[3].norm() - expected).abs() < 1e-10);
2194 }
2195
2196 #[test]
2197 fn test_rz_gate() {
2198 use std::f64::consts::PI;
2199
2200 let mut state1 = QulacsStateVector::new(1).unwrap();
2202 gates::pauli_x(&mut state1, 0).unwrap(); gates::rz(&mut state1, 0, PI).unwrap();
2204
2205 assert!(state1.amplitudes()[0].norm() < 1e-10);
2207 assert!((state1.amplitudes()[1].norm() - 1.0).abs() < 1e-10);
2208
2209 let mut state2 = QulacsStateVector::new(1).unwrap();
2211 gates::hadamard(&mut state2, 0).unwrap(); gates::rz(&mut state2, 0, PI / 4.0).unwrap();
2213
2214 assert!((state2.amplitudes()[0].norm() - 1.0 / 2.0f64.sqrt()).abs() < 1e-10);
2216 assert!((state2.amplitudes()[1].norm() - 1.0 / 2.0f64.sqrt()).abs() < 1e-10);
2217 }
2218
2219 #[test]
2220 fn test_phase_gate() {
2221 use std::f64::consts::PI;
2222
2223 let mut state = QulacsStateVector::new(1).unwrap();
2225 gates::pauli_x(&mut state, 0).unwrap(); gates::phase(&mut state, 0, PI / 2.0).unwrap(); assert!(state.amplitudes()[0].norm() < 1e-10);
2230 assert!((state.amplitudes()[1].re).abs() < 1e-10);
2231 assert!((state.amplitudes()[1].im - 1.0).abs() < 1e-10);
2232 }
2233
2234 #[test]
2235 fn test_u3_gate() {
2236 use std::f64::consts::PI;
2237
2238 let mut state1 = QulacsStateVector::new(1).unwrap();
2240 gates::u3(&mut state1, 0, PI / 2.0, 0.0, PI).unwrap();
2241
2242 let mut state2 = QulacsStateVector::new(1).unwrap();
2243 gates::hadamard(&mut state2, 0).unwrap();
2244
2245 let ratio = state1.amplitudes()[0] / state2.amplitudes()[0];
2247 assert!((state1.amplitudes()[0] / ratio - state2.amplitudes()[0]).norm() < 1e-10);
2248 assert!((state1.amplitudes()[1] / ratio - state2.amplitudes()[1]).norm() < 1e-10);
2249
2250 let mut state3 = QulacsStateVector::new(1).unwrap();
2252 gates::u3(&mut state3, 0, PI, 0.0, PI).unwrap();
2253
2254 let mut state4 = QulacsStateVector::new(1).unwrap();
2255 gates::pauli_x(&mut state4, 0).unwrap();
2256
2257 let ratio2 = state3.amplitudes()[1] / state4.amplitudes()[1];
2258 assert!((state3.amplitudes()[0] / ratio2 - state4.amplitudes()[0]).norm() < 1e-10);
2259 assert!((state3.amplitudes()[1] / ratio2 - state4.amplitudes()[1]).norm() < 1e-10);
2260 }
2261
2262 #[test]
2263 fn test_rotation_gates_on_multi_qubit_state() {
2264 use std::f64::consts::PI;
2265
2266 let mut state = QulacsStateVector::new(3).unwrap();
2268
2269 gates::ry(&mut state, 0, PI / 2.0).unwrap();
2271
2272 gates::rx(&mut state, 1, PI / 4.0).unwrap();
2274
2275 gates::rz(&mut state, 2, PI / 3.0).unwrap();
2277
2278 assert!((state.norm_squared() - 1.0).abs() < 1e-10);
2280 }
2281
2282 #[test]
2283 fn test_rotation_composition() {
2284 use std::f64::consts::PI;
2285
2286 let mut state1 = QulacsStateVector::new(1).unwrap();
2288 gates::rx(&mut state1, 0, PI / 4.0).unwrap();
2289 gates::ry(&mut state1, 0, PI / 4.0).unwrap();
2290 gates::rz(&mut state1, 0, PI / 4.0).unwrap();
2291
2292 assert!((state1.norm_squared() - 1.0).abs() < 1e-10);
2294
2295 let mut state2 = QulacsStateVector::new(1).unwrap();
2297 gates::u3(&mut state2, 0, PI / 4.0, PI / 4.0, PI / 4.0).unwrap();
2299
2300 assert!((state2.norm_squared() - 1.0).abs() < 1e-10);
2301 }
2302
2303 #[test]
2306 fn test_probability_calculation() {
2307 let state0 = QulacsStateVector::new(1).unwrap();
2309 assert!((state0.probability_zero(0).unwrap() - 1.0).abs() < 1e-10);
2310 assert!(state0.probability_one(0).unwrap().abs() < 1e-10);
2311
2312 let mut state1 = QulacsStateVector::new(1).unwrap();
2314 gates::pauli_x(&mut state1, 0).unwrap();
2315 assert!(state1.probability_zero(0).unwrap().abs() < 1e-10);
2316 assert!((state1.probability_one(0).unwrap() - 1.0).abs() < 1e-10);
2317
2318 let mut state_plus = QulacsStateVector::new(1).unwrap();
2320 gates::hadamard(&mut state_plus, 0).unwrap();
2321 assert!((state_plus.probability_zero(0).unwrap() - 0.5).abs() < 1e-10);
2322 assert!((state_plus.probability_one(0).unwrap() - 0.5).abs() < 1e-10);
2323 }
2324
2325 #[test]
2326 fn test_measurement_collapse() {
2327 let mut outcomes_0 = 0;
2329 let mut outcomes_1 = 0;
2330 let num_trials = 1000;
2331
2332 for _ in 0..num_trials {
2333 let mut state = QulacsStateVector::new(1).unwrap();
2334 gates::hadamard(&mut state, 0).unwrap();
2335
2336 let outcome = state.measure(0).unwrap();
2337 if outcome {
2338 outcomes_1 += 1;
2339 } else {
2340 outcomes_0 += 1;
2341 }
2342
2343 assert!((state.norm_squared() - 1.0).abs() < 1e-10);
2345 if outcome {
2346 assert!((state.probability_one(0).unwrap() - 1.0).abs() < 1e-10);
2347 } else {
2348 assert!((state.probability_zero(0).unwrap() - 1.0).abs() < 1e-10);
2349 }
2350 }
2351
2352 let ratio = outcomes_1 as f64 / num_trials as f64;
2354 assert!(ratio > 0.4 && ratio < 0.6, "Ratio: {}", ratio);
2355 }
2356
2357 #[test]
2358 fn test_sampling() {
2359 let mut bell_state = QulacsStateVector::new(2).unwrap();
2361 gates::hadamard(&mut bell_state, 0).unwrap();
2362 gates::cnot(&mut bell_state, 0, 1).unwrap();
2363
2364 let samples = bell_state.sample(1000).unwrap();
2366 assert_eq!(samples.len(), 1000);
2367
2368 let mut count_00 = 0;
2370 let mut count_11 = 0;
2371 for bitstring in &samples {
2372 assert_eq!(bitstring.len(), 2);
2373 if !bitstring[0] && !bitstring[1] {
2374 count_00 += 1;
2375 } else if bitstring[0] && bitstring[1] {
2376 count_11 += 1;
2377 } else {
2378 panic!("Unexpected outcome: {:?}", bitstring);
2380 }
2381 }
2382
2383 let ratio = count_00 as f64 / 1000.0;
2385 assert!(ratio > 0.4 && ratio < 0.6, "Ratio |00⟩: {}", ratio);
2386
2387 assert!((bell_state.norm_squared() - 1.0).abs() < 1e-10);
2389 }
2390
2391 #[test]
2392 fn test_get_counts() {
2393 let mut state = QulacsStateVector::new(1).unwrap();
2395 gates::hadamard(&mut state, 0).unwrap();
2396
2397 let counts = state.get_counts(1000).unwrap();
2398
2399 assert!(counts.len() <= 2);
2401
2402 let count_0 = *counts.get(&vec![false]).unwrap_or(&0);
2403 let count_1 = *counts.get(&vec![true]).unwrap_or(&0);
2404
2405 assert_eq!(count_0 + count_1, 1000);
2406
2407 let ratio = count_1 as f64 / 1000.0;
2409 assert!(ratio > 0.4 && ratio < 0.6, "Ratio |1⟩: {}", ratio);
2410 }
2411
2412 #[test]
2413 fn test_sample_qubits() {
2414 let mut bell = QulacsStateVector::new(2).unwrap();
2416 gates::hadamard(&mut bell, 0).unwrap();
2417 gates::cnot(&mut bell, 0, 1).unwrap();
2418
2419 let samples = bell.sample_qubits(&[0], 1000).unwrap();
2421
2422 let mut count_0 = 0;
2423 let mut count_1 = 0;
2424
2425 for bitstring in &samples {
2426 assert_eq!(bitstring.len(), 1);
2427 if bitstring[0] {
2428 count_1 += 1;
2429 } else {
2430 count_0 += 1;
2431 }
2432 }
2433
2434 let ratio = count_1 as f64 / 1000.0;
2436 assert!(ratio > 0.4 && ratio < 0.6, "Ratio: {}", ratio);
2437 }
2438
2439 #[test]
2440 fn test_measurement_multi_qubit() {
2441 let mut bell_state = QulacsStateVector::new(2).unwrap();
2443 gates::hadamard(&mut bell_state, 0).unwrap();
2444 gates::cnot(&mut bell_state, 0, 1).unwrap();
2445
2446 let outcome0 = bell_state.measure(0).unwrap();
2447 let outcome1 = bell_state.measure(1).unwrap();
2448
2449 assert_eq!(outcome0, outcome1);
2451 }
2452
2453 #[test]
2454 fn test_toffoli_gate() {
2455 let mut state = QulacsStateVector::new(3).unwrap();
2457
2458 gates::toffoli(&mut state, 0, 1, 2).unwrap();
2460 assert!((state.amplitudes()[0].norm() - 1.0).abs() < 1e-10);
2461 assert!(state.amplitudes()[7].norm() < 1e-10);
2462
2463 let mut state2 = QulacsStateVector::new(3).unwrap();
2465 gates::pauli_x(&mut state2, 0).unwrap(); gates::pauli_x(&mut state2, 1).unwrap(); gates::toffoli(&mut state2, 0, 1, 2).unwrap();
2470
2471 assert!((state2.amplitudes()[7].norm() - 1.0).abs() < 1e-10);
2473 assert!(state2.amplitudes()[3].norm() < 1e-10); let mut state3 = QulacsStateVector::new(3).unwrap();
2477 gates::hadamard(&mut state3, 0).unwrap();
2478
2479 gates::pauli_x(&mut state3, 0).unwrap();
2481 gates::pauli_x(&mut state3, 1).unwrap();
2482 gates::pauli_x(&mut state3, 2).unwrap();
2483
2484 gates::toffoli(&mut state3, 0, 1, 2).unwrap();
2486
2487 assert!((state3.norm_squared() - 1.0).abs() < 1e-10);
2489 }
2490
2491 #[test]
2492 fn test_toffoli_reversibility() {
2493 let mut state1 = QulacsStateVector::new(3).unwrap();
2495 gates::hadamard(&mut state1, 0).unwrap();
2496 gates::hadamard(&mut state1, 1).unwrap();
2497 gates::hadamard(&mut state1, 2).unwrap();
2498
2499 let original_state = state1.clone();
2500
2501 gates::toffoli(&mut state1, 0, 1, 2).unwrap();
2503 gates::toffoli(&mut state1, 0, 1, 2).unwrap();
2504
2505 for i in 0..8 {
2507 let diff = (state1.amplitudes()[i] - original_state.amplitudes()[i]).norm();
2508 assert!(diff < 1e-10, "Difference at index {}: {}", i, diff);
2509 }
2510 }
2511
2512 #[test]
2513 fn test_fredkin_gate() {
2514 let mut state = QulacsStateVector::new(3).unwrap();
2516
2517 gates::fredkin(&mut state, 0, 1, 2).unwrap();
2519 assert!((state.amplitudes()[0].norm() - 1.0).abs() < 1e-10);
2520
2521 let mut state2 = QulacsStateVector::new(3).unwrap();
2527 gates::pauli_x(&mut state2, 0).unwrap(); gates::pauli_x(&mut state2, 2).unwrap(); assert!((state2.amplitudes()[0b101].norm() - 1.0).abs() < 1e-10);
2532
2533 gates::fredkin(&mut state2, 0, 1, 2).unwrap();
2535
2536 assert!((state2.amplitudes()[0b011].norm() - 1.0).abs() < 1e-10);
2538 assert!(state2.amplitudes()[0b101].norm() < 1e-10);
2539
2540 let mut state3 = QulacsStateVector::new(3).unwrap();
2542 gates::pauli_x(&mut state3, 1).unwrap(); gates::pauli_x(&mut state3, 2).unwrap(); let before = state3.clone();
2547 gates::fredkin(&mut state3, 0, 1, 2).unwrap();
2548
2549 for i in 0..8 {
2551 let diff = (state3.amplitudes()[i] - before.amplitudes()[i]).norm();
2552 assert!(diff < 1e-10);
2553 }
2554 }
2555
2556 #[test]
2557 fn test_fredkin_reversibility() {
2558 let mut state1 = QulacsStateVector::new(3).unwrap();
2560 gates::hadamard(&mut state1, 0).unwrap();
2561 gates::hadamard(&mut state1, 1).unwrap();
2562 gates::hadamard(&mut state1, 2).unwrap();
2563
2564 let original_state = state1.clone();
2565
2566 gates::fredkin(&mut state1, 0, 1, 2).unwrap();
2568 gates::fredkin(&mut state1, 0, 1, 2).unwrap();
2569
2570 for i in 0..8 {
2572 let diff = (state1.amplitudes()[i] - original_state.amplitudes()[i]).norm();
2573 assert!(diff < 1e-10, "Difference at index {}: {}", i, diff);
2574 }
2575 }
2576
2577 #[test]
2578 fn test_toffoli_error_cases() {
2579 let mut state = QulacsStateVector::new(3).unwrap();
2580
2581 assert!(gates::toffoli(&mut state, 0, 1, 5).is_err());
2583 assert!(gates::toffoli(&mut state, 5, 1, 2).is_err());
2584
2585 assert!(gates::toffoli(&mut state, 0, 0, 2).is_err());
2587 assert!(gates::toffoli(&mut state, 0, 1, 1).is_err());
2588 assert!(gates::toffoli(&mut state, 0, 1, 0).is_err());
2589 }
2590
2591 #[test]
2592 fn test_fredkin_error_cases() {
2593 let mut state = QulacsStateVector::new(3).unwrap();
2594
2595 assert!(gates::fredkin(&mut state, 5, 1, 2).is_err());
2597 assert!(gates::fredkin(&mut state, 0, 5, 2).is_err());
2598 assert!(gates::fredkin(&mut state, 0, 1, 5).is_err());
2599
2600 assert!(gates::fredkin(&mut state, 0, 0, 2).is_err());
2602 assert!(gates::fredkin(&mut state, 0, 1, 1).is_err());
2603 assert!(gates::fredkin(&mut state, 0, 1, 0).is_err());
2604 }
2605
2606 #[test]
2607 fn test_s_gate() {
2608 let mut state = QulacsStateVector::new(1).unwrap();
2610 gates::pauli_x(&mut state, 0).unwrap(); gates::s(&mut state, 0).unwrap();
2612
2613 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2615 assert!((state.amplitudes()[1].re - 0.0).abs() < 1e-10);
2616 assert!((state.amplitudes()[1].im - 1.0).abs() < 1e-10);
2617 }
2618
2619 #[test]
2620 fn test_s_dag_gate() {
2621 let mut state = QulacsStateVector::new(1).unwrap();
2623 gates::pauli_x(&mut state, 0).unwrap(); gates::sdg(&mut state, 0).unwrap();
2625
2626 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2628 assert!((state.amplitudes()[1].re - 0.0).abs() < 1e-10);
2629 assert!((state.amplitudes()[1].im + 1.0).abs() < 1e-10);
2630 }
2631
2632 #[test]
2633 fn test_s_s_dag_identity() {
2634 let mut state = QulacsStateVector::new(1).unwrap();
2636 gates::hadamard(&mut state, 0).unwrap(); let original = state.clone();
2639
2640 gates::s(&mut state, 0).unwrap();
2641 gates::sdg(&mut state, 0).unwrap();
2642
2643 for i in 0..2 {
2645 let diff = (state.amplitudes()[i] - original.amplitudes()[i]).norm();
2646 assert!(diff < 1e-10);
2647 }
2648 }
2649
2650 #[test]
2651 fn test_t_gate() {
2652 let mut state = QulacsStateVector::new(1).unwrap();
2654 gates::pauli_x(&mut state, 0).unwrap(); gates::t(&mut state, 0).unwrap();
2656
2657 let expected = Complex64::from_polar(1.0, std::f64::consts::FRAC_PI_4);
2659 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2660 assert!((state.amplitudes()[1].re - expected.re).abs() < 1e-10);
2661 assert!((state.amplitudes()[1].im - expected.im).abs() < 1e-10);
2662 }
2663
2664 #[test]
2665 fn test_t_dag_gate() {
2666 let mut state = QulacsStateVector::new(1).unwrap();
2668 gates::pauli_x(&mut state, 0).unwrap(); gates::tdg(&mut state, 0).unwrap();
2670
2671 let expected = Complex64::from_polar(1.0, -std::f64::consts::FRAC_PI_4);
2673 assert!((state.amplitudes()[0].norm() - 0.0).abs() < 1e-10);
2674 assert!((state.amplitudes()[1].re - expected.re).abs() < 1e-10);
2675 assert!((state.amplitudes()[1].im - expected.im).abs() < 1e-10);
2676 }
2677
2678 #[test]
2679 fn test_t_t_dag_identity() {
2680 let mut state = QulacsStateVector::new(1).unwrap();
2682 gates::hadamard(&mut state, 0).unwrap(); let original = state.clone();
2685
2686 gates::t(&mut state, 0).unwrap();
2687 gates::tdg(&mut state, 0).unwrap();
2688
2689 for i in 0..2 {
2691 let diff = (state.amplitudes()[i] - original.amplitudes()[i]).norm();
2692 assert!(diff < 1e-10);
2693 }
2694 }
2695
2696 #[test]
2697 fn test_s_equals_two_t() {
2698 let mut state1 = QulacsStateVector::new(1).unwrap();
2700 let mut state2 = QulacsStateVector::new(1).unwrap();
2701
2702 gates::hadamard(&mut state1, 0).unwrap();
2703 gates::hadamard(&mut state2, 0).unwrap();
2704
2705 gates::s(&mut state1, 0).unwrap();
2707
2708 gates::t(&mut state2, 0).unwrap();
2710 gates::t(&mut state2, 0).unwrap();
2711
2712 for i in 0..2 {
2714 let diff = (state1.amplitudes()[i] - state2.amplitudes()[i]).norm();
2715 assert!(diff < 1e-10);
2716 }
2717 }
2718
2719 #[test]
2721 fn test_pauli_operator_matrices() {
2722 use observable::PauliOperator;
2723
2724 let i_mat = PauliOperator::I.matrix();
2726 assert_eq!(i_mat[[0, 0]], Complex64::new(1.0, 0.0));
2727 assert_eq!(i_mat[[1, 1]], Complex64::new(1.0, 0.0));
2728
2729 let x_mat = PauliOperator::X.matrix();
2731 assert_eq!(x_mat[[0, 1]], Complex64::new(1.0, 0.0));
2732 assert_eq!(x_mat[[1, 0]], Complex64::new(1.0, 0.0));
2733
2734 let z_mat = PauliOperator::Z.matrix();
2736 assert_eq!(z_mat[[0, 0]], Complex64::new(1.0, 0.0));
2737 assert_eq!(z_mat[[1, 1]], Complex64::new(-1.0, 0.0));
2738 }
2739
2740 #[test]
2741 fn test_pauli_observable_creation() {
2742 use observable::PauliObservable;
2743
2744 let obs_z = PauliObservable::pauli_z(&[0]);
2746 assert_eq!(obs_z.coefficient, 1.0);
2747 assert_eq!(obs_z.operators.len(), 1);
2748
2749 let obs_x = PauliObservable::pauli_x(&[0, 1]);
2751 assert_eq!(obs_x.operators.len(), 2);
2752 }
2753
2754 #[test]
2755 fn test_pauli_z_expectation_value() {
2756 use observable::PauliObservable;
2757
2758 let state = QulacsStateVector::new(1).unwrap();
2760
2761 let obs = PauliObservable::pauli_z(&[0]);
2763 let exp_val = obs.expectation_value(&state);
2764 assert!((exp_val - 1.0).abs() < 1e-10);
2765 }
2766
2767 #[test]
2768 fn test_pauli_z_expectation_value_excited() {
2769 use observable::PauliObservable;
2770
2771 let mut state = QulacsStateVector::new(1).unwrap();
2773 gates::pauli_x(&mut state, 0).unwrap();
2774
2775 let obs = PauliObservable::pauli_z(&[0]);
2777 let exp_val = obs.expectation_value(&state);
2778 assert!((exp_val - (-1.0)).abs() < 1e-10);
2779 }
2780
2781 #[test]
2782 fn test_pauli_z_expectation_value_superposition() {
2783 use observable::PauliObservable;
2784
2785 let mut state = QulacsStateVector::new(1).unwrap();
2787 gates::hadamard(&mut state, 0).unwrap();
2788
2789 let obs = PauliObservable::pauli_z(&[0]);
2791 let exp_val = obs.expectation_value(&state);
2792 assert!(exp_val.abs() < 1e-10);
2793 }
2794
2795 #[test]
2796 fn test_hermitian_observable() {
2797 use observable::HermitianObservable;
2798
2799 let mut matrix = Array2::zeros((2, 2));
2801 matrix[[0, 0]] = Complex64::new(1.0, 0.0);
2802 matrix[[1, 1]] = Complex64::new(-1.0, 0.0);
2803
2804 let obs = HermitianObservable::new(matrix).unwrap();
2805 assert_eq!(obs.num_qubits, 1);
2806
2807 let state = QulacsStateVector::new(1).unwrap();
2809 let exp_val = obs.expectation_value(&state).unwrap();
2810 assert!((exp_val - 1.0).abs() < 1e-10);
2811 }
2812
2813 #[test]
2814 fn test_composite_observable() {
2815 use observable::{CompositeObservable, PauliObservable};
2816
2817 let obs1 = PauliObservable::pauli_z(&[0]).with_coefficient(0.5);
2819 let obs2 = PauliObservable::pauli_z(&[1]).with_coefficient(0.3);
2820
2821 let composite = CompositeObservable::new().add_term(obs1).add_term(obs2);
2822
2823 assert_eq!(composite.num_terms(), 2);
2824
2825 let state = QulacsStateVector::new(2).unwrap();
2827 let exp_val = composite.expectation_value(&state);
2828 assert!((exp_val - 0.8).abs() < 1e-10);
2829 }
2830
2831 #[test]
2832 fn test_observable_weight() {
2833 use observable::{PauliObservable, PauliOperator};
2834 use std::collections::HashMap;
2835
2836 let mut operators = HashMap::new();
2837 operators.insert(0, PauliOperator::X);
2838 operators.insert(1, PauliOperator::Y);
2839 operators.insert(2, PauliOperator::I);
2840
2841 let obs = PauliObservable::new(operators, 1.0);
2842 assert_eq!(obs.weight(), 2); }
2844
2845 #[test]
2848 fn test_circuit_api_basic() {
2849 use circuit_api::QulacsCircuit;
2850
2851 let mut circuit = QulacsCircuit::new(2).unwrap();
2852 assert_eq!(circuit.num_qubits(), 2);
2853 assert_eq!(circuit.gate_count(), 0);
2854
2855 circuit.h(0).x(1);
2856 assert_eq!(circuit.gate_count(), 2);
2857 }
2858
2859 #[test]
2860 fn test_circuit_api_bell_state() {
2861 use circuit_api::QulacsCircuit;
2862
2863 let mut circuit = QulacsCircuit::new(2).unwrap();
2864 circuit.bell_pair(0, 1);
2865
2866 assert_eq!(circuit.gate_count(), 2);
2867
2868 let probs = circuit.probabilities();
2869 assert!((probs[0] - 0.5).abs() < 1e-10); assert!(probs[1].abs() < 1e-10); assert!(probs[2].abs() < 1e-10); assert!((probs[3] - 0.5).abs() < 1e-10); }
2874
2875 #[test]
2876 fn test_circuit_api_run_shots() {
2877 use circuit_api::QulacsCircuit;
2878
2879 let mut circuit = QulacsCircuit::new(2).unwrap();
2880 circuit.bell_pair(0, 1);
2881
2882 let counts = circuit.run(100).unwrap();
2883
2884 assert!(counts.contains_key("00") || counts.contains_key("11"));
2886 let total: usize = counts.values().sum();
2887 assert_eq!(total, 100);
2888 }
2889
2890 #[test]
2891 fn test_circuit_api_reset() {
2892 use circuit_api::QulacsCircuit;
2893
2894 let mut circuit = QulacsCircuit::new(2).unwrap();
2895 circuit.h(0).cnot(0, 1);
2896 assert_eq!(circuit.gate_count(), 2);
2897
2898 circuit.reset().unwrap();
2899 assert_eq!(circuit.gate_count(), 0);
2900
2901 let probs = circuit.probabilities();
2903 assert!((probs[0] - 1.0).abs() < 1e-10);
2904 }
2905
2906 #[test]
2907 fn test_circuit_api_rotation_gates() {
2908 use circuit_api::QulacsCircuit;
2909 use std::f64::consts::PI;
2910
2911 let mut circuit = QulacsCircuit::new(1).unwrap();
2912
2913 circuit.rx(0, PI);
2915 let probs = circuit.probabilities();
2916 assert!(probs[0].abs() < 1e-10);
2917 assert!((probs[1] - 1.0).abs() < 1e-10);
2918 }
2919
2920 #[test]
2921 fn test_circuit_api_phase_gates() {
2922 use circuit_api::QulacsCircuit;
2923
2924 let mut circuit = QulacsCircuit::new(1).unwrap();
2925
2926 circuit.s(0).s(0);
2928 assert_eq!(circuit.gate_count(), 2);
2929
2930 let probs = circuit.probabilities();
2932 assert!((probs[0] - 1.0).abs() < 1e-10);
2933 }
2934
2935 #[test]
2936 fn test_circuit_api_two_qubit_gates() {
2937 use circuit_api::QulacsCircuit;
2938
2939 let mut circuit = QulacsCircuit::new(2).unwrap();
2940
2941 circuit.x(0).cnot(0, 1);
2943
2944 let probs = circuit.probabilities();
2945 assert!(probs[0].abs() < 1e-10); assert!(probs[1].abs() < 1e-10); assert!(probs[2].abs() < 1e-10); assert!((probs[3] - 1.0).abs() < 1e-10); }
2950
2951 #[test]
2952 fn test_circuit_api_observable() {
2953 use circuit_api::{Observable, QulacsCircuit};
2954 use observable::PauliObservable;
2955
2956 let mut circuit = QulacsCircuit::new(2).unwrap();
2957 circuit.h(0).h(1);
2958
2959 let obs = PauliObservable::pauli_z(&[0]);
2960 let exp_val = circuit.expectation(&obs).unwrap();
2961
2962 assert!(exp_val.abs() < 1e-10);
2964 }
2965
2966 #[test]
2967 fn test_circuit_api_gate_record() {
2968 use circuit_api::QulacsCircuit;
2969
2970 let mut circuit = QulacsCircuit::new(2).unwrap();
2971 circuit.h(0).cnot(0, 1).rx(1, 1.5);
2972
2973 let gates = circuit.gates();
2974 assert_eq!(gates.len(), 3);
2975 assert_eq!(gates[0].name, "H");
2976 assert_eq!(gates[0].qubits, vec![0]);
2977 assert_eq!(gates[1].name, "CNOT");
2978 assert_eq!(gates[1].qubits, vec![0, 1]);
2979 assert_eq!(gates[2].name, "RX");
2980 assert_eq!(gates[2].params[0], 1.5);
2981 }
2982
2983 #[test]
2984 fn test_circuit_api_noise_model() {
2985 use crate::noise_models::{DepolarizingNoise, NoiseModel as KrausNoiseModel};
2986 use circuit_api::QulacsCircuit;
2987 use std::sync::Arc;
2988
2989 let mut circuit = QulacsCircuit::new(2).unwrap();
2990 assert!(!circuit.has_noise_model());
2991
2992 let mut noise_model = KrausNoiseModel::new();
2994 noise_model.add_channel(Arc::new(DepolarizingNoise::new(0.01)));
2995
2996 circuit.set_noise_model(noise_model);
2997 assert!(circuit.has_noise_model());
2998
2999 circuit.h(0).cnot(0, 1);
3001 assert_eq!(circuit.gate_count(), 2);
3002
3003 circuit.clear_noise_model();
3005 assert!(!circuit.has_noise_model());
3006 }
3007
3008 #[test]
3009 fn test_circuit_api_run_with_noise() {
3010 use crate::noise_models::{BitFlipNoise, NoiseModel as KrausNoiseModel};
3011 use circuit_api::QulacsCircuit;
3012 use std::sync::Arc;
3013
3014 let mut circuit = QulacsCircuit::new(1).unwrap();
3015
3016 let mut noise_model = KrausNoiseModel::new();
3018 noise_model.add_channel(Arc::new(BitFlipNoise::new(0.1)));
3019 circuit.set_noise_model(noise_model);
3020
3021 let counts = circuit.run_with_noise(100).unwrap();
3023
3024 let total: usize = counts.values().sum();
3026 assert_eq!(total, 100);
3027
3028 assert!(counts.contains_key("0"));
3031 }
3032}