1use crate::circuit::Measurement;
30use crate::circuit::circuit_impl::Circuit;
31use crate::circuit::circuit_param::CircuitParam;
32use crate::circuit::error::CircuitError;
33use crate::circuit::gate::StandardGate;
34use crate::circuit::gate::directive::Directive;
35use crate::circuit::gate::instruction::Instruction;
36use crate::device::{ExecutionResult, Outcome};
37use crate::qis::Observable;
38use crate::qis::error::QisError;
39use num_complex::Complex64;
40use rand::{Rng, SeedableRng, rngs::SmallRng};
41use rayon::prelude::*;
42use smallvec::{SmallVec, smallvec};
43use std::collections::{HashMap, HashSet};
44use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4};
45
46const PARALLEL_THRESHOLD: usize = 7;
47
48macro_rules! with_maybe_par {
49 ($num_qubits:expr, $data:expr, $chunk_size:expr, $body:expr) => {{
50 if $num_qubits < PARALLEL_THRESHOLD {
51 $data.chunks_exact_mut($chunk_size).for_each($body);
52 } else {
53 $data.par_chunks_exact_mut($chunk_size).for_each($body);
54 }
55 }};
56}
57
58#[inline(always)]
59fn interleave_bits(keep: &[usize], k_val: usize, trace: &[usize], t_val: usize) -> usize {
60 let mut res = 0;
61 for (idx, &q) in keep.iter().enumerate() {
62 if (k_val >> idx) & 1 == 1 {
63 res |= 1 << q;
64 }
65 }
66 for (idx, &q) in trace.iter().enumerate() {
67 if (t_val >> idx) & 1 == 1 {
68 res |= 1 << q;
69 }
70 }
71 res
72}
73
74#[derive(Debug, Clone)]
105pub struct DensityMatrix {
106 data: Vec<Complex64>,
108 pub(super) qubit_map: super::QubitMap,
110 pub num_qubits: usize,
112}
113
114impl std::ops::AddAssign for DensityMatrix {
115 fn add_assign(&mut self, rhs: Self) {
116 assert_eq!(self.num_qubits, rhs.num_qubits);
117 if self.num_qubits < PARALLEL_THRESHOLD {
118 self.data
119 .iter_mut()
120 .zip(rhs.data)
121 .for_each(|(a, b)| *a += b);
122 } else {
123 self.data
124 .par_iter_mut()
125 .zip(rhs.data.into_par_iter())
126 .for_each(|(a, b)| *a += b);
127 }
128 }
129}
130
131impl DensityMatrix {
132 pub fn data(&self) -> &[Complex64] {
134 &self.data
135 }
136
137 #[inline]
139 fn validate_qubit(&self, qubit: usize) -> Result<(), QisError> {
140 if self.num_qubits == 0 {
141 return Err(QisError::InvalidParameterValue(
142 "no valid qubit indices in a zero-qubit system".to_string(),
143 ));
144 }
145 if qubit >= self.num_qubits {
146 return Err(QisError::IndexOutOfBounds {
147 index: qubit,
148 max: self.num_qubits.saturating_sub(1),
149 });
150 }
151 Ok(())
152 }
153
154 #[inline]
156 fn validate_two_qubits(&self, q0: usize, q1: usize) -> Result<(), QisError> {
157 if self.num_qubits == 0 {
158 return Err(QisError::InvalidParameterValue(
159 "no valid qubit indices in a zero-qubit system".to_string(),
160 ));
161 }
162 if q0 >= self.num_qubits {
163 return Err(QisError::IndexOutOfBounds {
164 index: q0,
165 max: self.num_qubits.saturating_sub(1),
166 });
167 }
168 if q1 >= self.num_qubits {
169 return Err(QisError::IndexOutOfBounds {
170 index: q1,
171 max: self.num_qubits.saturating_sub(1),
172 });
173 }
174 if q0 == q1 {
175 return Err(QisError::InvalidParameterValue(format!(
176 "Duplicate qubit index {q0} in gate operation"
177 )));
178 }
179 Ok(())
180 }
181
182 #[inline]
184 fn validate_qubits(&self, qubits: &[usize]) -> Result<(), QisError> {
185 if self.num_qubits == 0 {
186 return Err(QisError::InvalidParameterValue(
187 "no valid qubit indices in a zero-qubit system".to_string(),
188 ));
189 }
190 for &q in qubits {
191 if q >= self.num_qubits {
192 return Err(QisError::IndexOutOfBounds {
193 index: q,
194 max: self.num_qubits.saturating_sub(1),
195 });
196 }
197 }
198 for (i, &q) in qubits.iter().enumerate() {
199 if qubits[..i].contains(&q) {
200 return Err(QisError::InvalidParameterValue(format!(
201 "Duplicate qubit index {q} in gate operation"
202 )));
203 }
204 }
205 Ok(())
206 }
207
208 pub fn new(num_qubits: usize) -> Self {
218 Self::try_new(num_qubits).expect("cannot allocate density matrix")
219 }
220
221 pub fn try_new(num_qubits: usize) -> Result<Self, QisError> {
229 let size = super::checked_state_len(num_qubits, true)?;
230 let mut data = Vec::new();
231 data.try_reserve_exact(size).map_err(|_| {
232 QisError::UnsupportedOperation("density matrix allocation failed".into())
233 })?;
234 data.resize(size, Complex64::new(0.0, 0.0));
235 data[0] = Complex64::new(1.0, 0.0);
236 Ok(Self {
237 data,
238 num_qubits,
239 qubit_map: None,
240 })
241 }
242
243 pub fn maximally_mixed(num_qubits: usize) -> Self {
257 Self::try_maximally_mixed(num_qubits).expect("cannot allocate density matrix")
258 }
259
260 pub fn try_maximally_mixed(num_qubits: usize) -> Result<Self, QisError> {
267 let mut state = Self::try_new(num_qubits)?;
268 let dim = 1usize << num_qubits;
269 let probability = Complex64::new(1.0 / dim as f64, 0.0);
270 for index in 0..dim {
271 state.data[index * dim + index] = probability;
272 }
273 Ok(state)
274 }
275
276 pub fn from_state(num_qubits: usize, initial_state: Vec<Complex64>) -> Result<Self, QisError> {
288 super::checked_state_len(num_qubits, true)?;
289 let dim = 1usize << num_qubits;
290 if initial_state.len() != dim {
291 return Err(QisError::InvalidStateDimension(initial_state.len()));
292 }
293 let norm: f64 = initial_state.iter().map(|c| c.norm_sqr()).sum();
294 if !norm.is_finite() || (norm - 1.0).abs() >= 1e-10 {
295 return Err(QisError::NotNormalized);
296 }
297
298 let mut data = Self::try_new(num_qubits)?.data;
299
300 let kernel = |(i, chunk): (usize, &mut [Complex64])| {
301 let alpha_i = initial_state[i];
302 for j in 0..dim {
303 chunk[j] = alpha_i * initial_state[j].conj();
304 }
305 };
306
307 if num_qubits < PARALLEL_THRESHOLD {
308 data.chunks_exact_mut(dim).enumerate().for_each(kernel);
309 } else {
310 data.par_chunks_exact_mut(dim).enumerate().for_each(kernel);
311 }
312
313 Ok(Self {
314 data,
315 num_qubits,
316 qubit_map: None,
317 })
318 }
319
320 pub fn from_density_matrix_state(
334 num_qubits: usize,
335 dm_state: Vec<Complex64>,
336 ) -> Result<Self, QisError> {
337 let size = super::checked_state_len(num_qubits, true)?;
338 if dm_state.len() != size {
339 return Err(QisError::InvalidStateDimension(dm_state.len()));
340 }
341
342 let dm = Self {
343 data: dm_state,
344 num_qubits,
345 qubit_map: None,
346 };
347 dm.validate_physical(1e-10)?;
348 Ok(dm)
349 }
350
351 pub fn probabilities(&self) -> Vec<f64> {
359 let dim = 1 << self.num_qubits;
360 if self.num_qubits < PARALLEL_THRESHOLD {
361 (0..dim).map(|i| self.data[i * dim + i].re).collect()
362 } else {
363 (0..dim)
364 .into_par_iter()
365 .map(|i| self.data[i * dim + i].re)
366 .collect()
367 }
368 }
369
370 pub fn from_circuit(circuit: &Circuit) -> Result<Self, QisError> {
381 let mut dm = DensityMatrix::try_new(circuit.num_qubits())?;
382 dm.apply_circuit(circuit)?;
383 Ok(dm)
384 }
385
386 pub fn apply_circuit(&mut self, circuit: &Circuit) -> Result<(), QisError> {
422 if self.num_qubits != circuit.num_qubits() {
423 return Err(QisError::InvalidStateDimension(circuit.num_qubits()));
424 }
425 if circuit
426 .operations()
427 .iter()
428 .any(|op| matches!(op.instruction, Instruction::ClassicalControl(_)))
429 {
430 return Err(QisError::UnsupportedOperation(
431 "classical control flow is not supported in density matrix simulation".to_string(),
432 ));
433 }
434 let circuit = circuit.decompose()?;
435 super::validate_terminal_measurements(&circuit)?;
436 let dm = self;
437
438 let qubit_map = super::prepare_qubit_map(&circuit.qubits(), &dm.qubit_map)?;
439
440 let parameter_values: Vec<Option<f64>> = circuit
441 .parameters()
442 .iter()
443 .map(|p| p.evaluate(&None).ok())
444 .collect();
445
446 for op in circuit.operations() {
447 let params: Vec<f64> = op
448 .params
449 .iter()
450 .map(|p| match p {
451 CircuitParam::Fixed(v) => Ok(*v),
452 CircuitParam::Index(idx) => parameter_values
453 .get(*idx as usize)
454 .copied()
455 .flatten()
456 .ok_or(QisError::CircuitError(CircuitError::SymbolicParameterError)),
457 })
458 .collect::<Result<Vec<_>, QisError>>()?;
459
460 let qubit_indices: Result<Vec<usize>, QisError> =
461 op.qubits
462 .iter()
463 .map(|q| {
464 qubit_map.get(q).copied().ok_or_else(|| {
465 QisError::CircuitError(CircuitError::QubitNotFound(q.id()))
466 })
467 })
468 .collect();
469 let qubit_indices = qubit_indices?;
470
471 match &op.instruction {
472 Instruction::Standard(gate) => {
473 dm.apply_standard_gate(*gate, &qubit_indices, ¶ms)?;
474 }
475 Instruction::McGate(mc_gate) => {
476 let num_controls = mc_gate.num_ctrl_qubits();
477 let base_gate = mc_gate.base_gate();
478
479 match (num_controls, *base_gate) {
480 (0, StandardGate::GPhase) => {
481 dm.apply_standard_gate(*base_gate, &qubit_indices, ¶ms)?
482 }
483 (1, StandardGate::X) => dm.apply_cx(qubit_indices[0], qubit_indices[1])?,
484 (1, StandardGate::Y) => dm.apply_cy(qubit_indices[0], qubit_indices[1])?,
485 (1, StandardGate::Z) => dm.apply_cz(qubit_indices[0], qubit_indices[1])?,
486 (1, StandardGate::RX) => {
487 dm.apply_crx(qubit_indices[0], qubit_indices[1], params[0])?
488 }
489 (1, StandardGate::RY) => {
490 dm.apply_cry(qubit_indices[0], qubit_indices[1], params[0])?
491 }
492 (1, StandardGate::RZ) => {
493 dm.apply_crz(qubit_indices[0], qubit_indices[1], params[0])?
494 }
495 (2, StandardGate::X) => {
496 dm.apply_ccx(qubit_indices[0], qubit_indices[1], qubit_indices[2])?
497 }
498 _ => {
499 let matrix = mc_gate.matrix(¶ms).map_err(|_| {
500 QisError::CircuitError(CircuitError::NoMatrixRepresentation)
501 })?;
502 dm.apply_unitary_gate(&qubit_indices, &matrix)?;
503 }
504 }
505 }
506 Instruction::UnitaryGate(u_gate) => {
507 if let Some(matrix) = u_gate.matrix() {
508 dm.apply_unitary_gate(&qubit_indices, matrix)?;
509 } else {
510 return Err(QisError::CircuitError(CircuitError::NoMatrixRepresentation));
511 }
512 }
513 Instruction::CircuitGate(_) => {
514 return Err(QisError::CircuitError(CircuitError::InvalidOperation(
515 "CircuitGate should have been decomposed".to_string(),
516 )));
517 }
518 Instruction::Directive(directive) => match directive {
519 Directive::Barrier => {}
520 Directive::Measure => {
521 return Err(QisError::UnsupportedOperation(
522 "legacy Measure directive is not supported by density matrix circuit evolution; use Circuit::measure as an output declaration".to_string(),
523 ));
524 }
525 Directive::Reset => {
526 let qubit = qubit_indices[0];
527 dm.reset(qubit)?;
528 }
529 },
530 Instruction::Delay => continue,
531 Instruction::ClassicalControl(_) => {
532 return Err(QisError::UnsupportedOperation(
533 "classical control flow is not supported in density matrix simulation"
534 .to_string(),
535 ));
536 }
537 Instruction::ClassicalData(_) => {}
538 }
539 }
540 dm.qubit_map = Some(qubit_map);
541 Ok(())
542 }
543
544 pub fn apply_standard_gate(
545 &mut self,
546 gate: StandardGate,
547 qubits: &[usize],
548 params: &[f64],
549 ) -> Result<(), QisError> {
550 if qubits.len() != gate.num_qubits() {
551 return Err(QisError::InvalidParameterValue(format!(
552 "Gate {:?} requires {} qubits, got {}",
553 gate,
554 gate.num_qubits(),
555 qubits.len()
556 )));
557 }
558 if params.len() != gate.num_params() {
559 return Err(QisError::InvalidParameterValue(format!(
560 "Gate {:?} requires {} parameters, got {}",
561 gate,
562 gate.num_params(),
563 params.len()
564 )));
565 }
566 match gate {
567 StandardGate::I => {}
568 StandardGate::X => self.apply_x(qubits[0])?,
569 StandardGate::Y => self.apply_y(qubits[0])?,
570 StandardGate::Z => self.apply_z(qubits[0])?,
571 StandardGate::H => self.apply_h(qubits[0])?,
572 StandardGate::S => self.apply_s(qubits[0])?,
573 StandardGate::SDG => self.apply_sdg(qubits[0])?,
574 StandardGate::T => self.apply_t(qubits[0])?,
575 StandardGate::TDG => self.apply_tdg(qubits[0])?,
576 StandardGate::RX => self.apply_rx(qubits[0], params[0])?,
577 StandardGate::RY => self.apply_ry(qubits[0], params[0])?,
578 StandardGate::RZ => self.apply_rz(qubits[0], params[0])?,
579 StandardGate::Phase => self.apply_phase(qubits[0], params[0])?,
580 StandardGate::X2P => self.apply_x2p(qubits[0])?,
581 StandardGate::X2M => self.apply_x2m(qubits[0])?,
582 StandardGate::Y2P => self.apply_y2p(qubits[0])?,
583 StandardGate::Y2M => self.apply_y2m(qubits[0])?,
584 StandardGate::RXY => self.apply_rxy(qubits[0], params[0], params[1])?,
585 StandardGate::XY => self.apply_xy(qubits[0], params[0])?,
586 StandardGate::XY2P => self.apply_xy2p(qubits[0], params[0])?,
587 StandardGate::XY2M => self.apply_xy2m(qubits[0], params[0])?,
588 StandardGate::U => self.apply_u(qubits[0], params[0], params[1], params[2])?,
589 StandardGate::GPhase => self.apply_gphase(params[0]),
590
591 StandardGate::CX => self.apply_cx(qubits[0], qubits[1])?,
592 StandardGate::CY => self.apply_cy(qubits[0], qubits[1])?,
593 StandardGate::CZ => self.apply_cz(qubits[0], qubits[1])?,
594 StandardGate::SWAP => self.apply_swap(qubits[0], qubits[1])?,
595 StandardGate::RXX => self.apply_rxx(qubits[0], qubits[1], params[0])?,
596 StandardGate::RYY => self.apply_ryy(qubits[0], qubits[1], params[0])?,
597 StandardGate::RZZ => self.apply_rzz(qubits[0], qubits[1], params[0])?,
598 StandardGate::RZX => self.apply_rzx(qubits[0], qubits[1], params[0])?,
599
600 StandardGate::CRX => self.apply_crx(qubits[0], qubits[1], params[0])?,
601 StandardGate::CRY => self.apply_cry(qubits[0], qubits[1], params[0])?,
602 StandardGate::CRZ => self.apply_crz(qubits[0], qubits[1], params[0])?,
603
604 StandardGate::CCX => self.apply_ccx(qubits[0], qubits[1], qubits[2])?,
605
606 StandardGate::FSIM => self.apply_fsim(qubits[0], qubits[1], params[0], params[1])?,
607 }
608 Ok(())
609 }
610
611 pub fn zeros(num_qubits: usize) -> Self {
624 Self::try_zeros(num_qubits).expect("cannot allocate density matrix")
625 }
626
627 pub fn try_zeros(num_qubits: usize) -> Result<Self, QisError> {
634 let mut state = Self::try_new(num_qubits)?;
635 state.data[0] = Complex64::new(0.0, 0.0);
636 Ok(state)
637 }
638
639 pub fn trace(&self) -> Complex64 {
646 let n = self.num_qubits;
647 let dim = 1 << n;
648 if n < PARALLEL_THRESHOLD {
649 (0..dim).map(|i| self.data[i * dim + i]).sum()
650 } else {
651 (0..dim)
652 .into_par_iter()
653 .map(|i| self.data[i * dim + i])
654 .sum()
655 }
656 }
657
658 pub fn is_hermitian(&self, tol: f64) -> bool {
668 if self
669 .data
670 .iter()
671 .any(|v| !v.re.is_finite() || !v.im.is_finite())
672 {
673 return false;
674 }
675 let dim = 1 << self.num_qubits;
676 for i in 0..dim {
677 for j in 0..i {
678 let ij_idx = i * dim + j;
679 let ji_idx = j * dim + i;
680 let ij = self.data[ij_idx];
681 let ji_conj = self.data[ji_idx].conj();
682 if (ij.re - ji_conj.re).abs() > tol || (ij.im - ji_conj.im).abs() > tol {
683 return false;
684 }
685 }
686 }
687 for i in 0..dim {
689 if self.data[i * dim + i].im.abs() > tol {
690 return false;
691 }
692 }
693 true
694 }
695
696 pub fn is_positive_semidefinite_approx(&self, tol: f64) -> bool {
716 let dim = 1 << self.num_qubits;
717
718 if self
720 .data
721 .iter()
722 .any(|c| c.re.is_nan() || c.re.is_infinite() || c.im.is_nan() || c.im.is_infinite())
723 {
724 return false;
725 }
726
727 if !self.is_hermitian(tol) {
728 return false;
729 }
730
731 for i in 0..dim {
733 if self.data[i * dim + i].re < -tol {
734 return false;
735 }
736 }
737
738 let mat = faer::Mat::from_fn(dim, dim, |row, col| {
740 let c = self.data[row * dim + col];
741 faer::c64::new(c.re, c.im)
742 });
743
744 match mat.self_adjoint_eigenvalues(faer::Side::Lower) {
745 Ok(eigenvalues) => eigenvalues.iter().all(|&lambda| lambda >= -tol),
746 Err(_) => false,
747 }
748 }
749
750 pub fn validate_physical(&self, tol: f64) -> Result<(), QisError> {
766 if !self.is_hermitian(tol) {
768 return Err(QisError::NotHermitian);
769 }
770
771 if !self.is_positive_semidefinite_approx(tol) {
773 return Err(QisError::NotPositiveSemidefinite);
774 }
775
776 let tr = self.trace();
778 if (tr.re - 1.0).abs() >= tol || tr.im.abs() >= tol {
779 return Err(QisError::NotNormalized);
780 }
781
782 Ok(())
783 }
784
785 fn apply_x_kernel(&mut self, q: usize, off: usize) {
786 let d = 1 << (q + off);
787 let kernel = |chunk: &mut [Complex64]| {
788 let (l, u) = chunk.split_at_mut(d);
789 l.iter_mut()
790 .zip(u.iter_mut())
791 .for_each(|(a, b)| std::mem::swap(a, b));
792 };
793 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
794 }
795
796 fn apply_y_kernel(&mut self, q: usize, off: usize, conj: bool) {
797 let d = 1 << (q + off);
798 let kernel = |chunk: &mut [Complex64]| {
799 let (l, u) = chunk.split_at_mut(d);
800 l.iter_mut().zip(u.iter_mut()).for_each(|(alpha, beta)| {
801 let a = *alpha;
802 let b = *beta;
803 if conj {
804 *alpha = Complex64::new(-b.im, b.re);
805 *beta = Complex64::new(a.im, -a.re);
806 } else {
807 *alpha = Complex64::new(b.im, -b.re);
808 *beta = Complex64::new(-a.im, a.re);
809 }
810 });
811 };
812 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
813 }
814
815 fn apply_z_kernel(&mut self, q: usize, off: usize) {
816 let d = 1 << (q + off);
817 let kernel = |chunk: &mut [Complex64]| {
818 u_slice(chunk, d).iter_mut().for_each(|v| *v = -*v);
819 };
820 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
821 }
822
823 fn apply_h_kernel(&mut self, q: usize, off: usize) {
824 let d = 1 << (q + off);
825 let k = FRAC_1_SQRT_2;
826 let kernel = |chunk: &mut [Complex64]| {
827 let (l, u) = chunk.split_at_mut(d);
828 l.iter_mut().zip(u.iter_mut()).for_each(|(a, b)| {
829 let (v0, v1) = (*a, *b);
830 *a = k * (v0 + v1);
831 *b = k * (v0 - v1);
832 });
833 };
834 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
835 }
836
837 fn apply_rx_kernel(&mut self, q: usize, off: usize, theta: f64) {
838 let d = 1 << (q + off);
839 let (c, s) = ((theta * 0.5).cos(), (theta * 0.5).sin());
840 let kernel = |chunk: &mut [Complex64]| {
841 let (l, u) = chunk.split_at_mut(d);
842 l.iter_mut().zip(u.iter_mut()).for_each(|(alpha, beta)| {
843 let (a, b) = (*alpha, *beta);
844 *alpha = Complex64::new(a.re * c + b.im * s, a.im * c - b.re * s);
845 *beta = Complex64::new(b.re * c + a.im * s, b.im * c - a.re * s);
846 });
847 };
848 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
849 }
850
851 fn apply_ry_kernel(&mut self, q: usize, off: usize, theta: f64) {
852 let d = 1 << (q + off);
853 let (c, s) = ((theta * 0.5).cos(), (theta * 0.5).sin());
854 let kernel = |chunk: &mut [Complex64]| {
855 let (l, u) = chunk.split_at_mut(d);
856 l.iter_mut().zip(u.iter_mut()).for_each(|(a, b)| {
857 let (v0, v1) = (*a, *b);
858 *a = v0 * c - v1 * s;
859 *b = v0 * s + v1 * c;
860 });
861 };
862 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
863 }
864
865 fn apply_u_kernel(&mut self, q: usize, off: usize, theta: f64, phi: f64, lam: f64, conj: bool) {
866 let d = 1 << (q + off);
867 let (c, s) = ((theta * 0.5).cos(), (theta * 0.5).sin());
868 let (p_phi, p_lam, p_tot) = if conj {
869 (
870 Complex64::from_polar(1.0, -phi),
871 Complex64::from_polar(1.0, -lam),
872 Complex64::from_polar(1.0, -(phi + lam)),
873 )
874 } else {
875 (
876 Complex64::from_polar(1.0, phi),
877 Complex64::from_polar(1.0, lam),
878 Complex64::from_polar(1.0, phi + lam),
879 )
880 };
881 let (u00, u01, u10, u11) = (Complex64::new(c, 0.0), -p_lam * s, p_phi * s, p_tot * c);
882 let kernel = |chunk: &mut [Complex64]| {
883 let (l, u) = chunk.split_at_mut(d);
884 l.iter_mut().zip(u.iter_mut()).for_each(|(a, b)| {
885 let (v0, v1) = (*a, *b);
886 *a = u00 * v0 + u01 * v1;
887 *b = u10 * v0 + u11 * v1;
888 });
889 };
890 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
891 }
892
893 fn apply_cx_kernel(&mut self, ctrl: usize, tgt: usize, off: usize) {
894 let (c, t) = (ctrl + off, tgt + off);
895 let (q_min, q_max) = if c < t { (c, t) } else { (t, c) };
896 let (d_min, d_max) = (1 << q_min, 1 << q_max);
897 let kernel = |chunk: &mut [Complex64]| {
898 let (p0, p1) = chunk.split_at_mut(d_max);
899 p0.chunks_exact_mut(2 * d_min)
900 .zip(p1.chunks_exact_mut(2 * d_min))
901 .for_each(|(s0, s1)| {
902 if c < t {
903 let (_, v01) = s0.split_at_mut(d_min);
904 let (_, v11) = s1.split_at_mut(d_min);
905 v01.iter_mut()
906 .zip(v11.iter_mut())
907 .for_each(|(a, b)| std::mem::swap(a, b));
908 } else {
909 let (v10, v11) = s1.split_at_mut(d_min);
910 v10.iter_mut()
911 .zip(v11.iter_mut())
912 .for_each(|(a, b)| std::mem::swap(a, b));
913 }
914 });
915 };
916 with_maybe_par!(self.num_qubits, self.data, 2 * d_max, kernel);
917 }
918
919 fn apply_cz_kernel(&mut self, q0: usize, q1: usize, off: usize) {
920 let (c0, c1) = (q0 + off, q1 + off);
921 let (q_min, q_max) = if c0 < c1 { (c0, c1) } else { (c1, c0) };
922 let (d_min, d_max) = (1 << q_min, 1 << q_max);
923 let kernel = |chunk: &mut [Complex64]| {
924 let (_, p1) = chunk.split_at_mut(d_max);
925 p1.chunks_exact_mut(2 * d_min).for_each(|s| {
926 u_slice(s, d_min).iter_mut().for_each(|v| *v = -*v);
927 });
928 };
929 with_maybe_par!(self.num_qubits, self.data, 2 * d_max, kernel);
930 }
931
932 pub fn apply_x(&mut self, qubit: usize) -> Result<(), QisError> {
937 self.validate_qubit(qubit)?;
938 self.apply_x_kernel(qubit, self.num_qubits);
939 self.apply_x_kernel(qubit, 0);
940 Ok(())
941 }
942 pub fn apply_y(&mut self, qubit: usize) -> Result<(), QisError> {
944 self.validate_qubit(qubit)?;
945 self.apply_y_kernel(qubit, self.num_qubits, false);
946 self.apply_y_kernel(qubit, 0, true);
947 Ok(())
948 }
949 pub fn apply_z(&mut self, qubit: usize) -> Result<(), QisError> {
951 self.validate_qubit(qubit)?;
952 self.apply_z_kernel(qubit, self.num_qubits);
953 self.apply_z_kernel(qubit, 0);
954 Ok(())
955 }
956 pub fn apply_h(&mut self, qubit: usize) -> Result<(), QisError> {
958 self.validate_qubit(qubit)?;
959 self.apply_h_kernel(qubit, self.num_qubits);
960 self.apply_h_kernel(qubit, 0);
961 Ok(())
962 }
963 pub fn apply_u(
965 &mut self,
966 qubit: usize,
967 theta: f64,
968 phi: f64,
969 lambda: f64,
970 ) -> Result<(), QisError> {
971 self.validate_qubit(qubit)?;
972 self.apply_u_kernel(qubit, self.num_qubits, theta, phi, lambda, false);
973 self.apply_u_kernel(qubit, 0, theta, phi, lambda, true);
974 Ok(())
975 }
976 pub fn apply_rx(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
978 self.validate_qubit(qubit)?;
979 self.apply_rx_kernel(qubit, self.num_qubits, theta);
980 self.apply_rx_kernel(qubit, 0, -theta);
981 Ok(())
982 }
983 pub fn apply_ry(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
985 self.validate_qubit(qubit)?;
986 self.apply_ry_kernel(qubit, self.num_qubits, theta);
987 self.apply_ry_kernel(qubit, 0, theta);
988 Ok(())
989 }
990 pub fn apply_rz(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
992 self.apply_phase(qubit, theta)?; Ok(())
994 }
995 pub fn apply_phase(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
997 self.validate_qubit(qubit)?;
998 self.apply_p_kernel(qubit, self.num_qubits, theta);
999 self.apply_p_kernel(qubit, 0, -theta);
1000 Ok(())
1001 }
1002
1003 pub fn apply_s(&mut self, qubit: usize) -> Result<(), QisError> {
1005 self.apply_phase(qubit, FRAC_PI_2)?;
1006 Ok(())
1007 }
1008 pub fn apply_sdg(&mut self, qubit: usize) -> Result<(), QisError> {
1010 self.apply_phase(qubit, -FRAC_PI_2)?;
1011 Ok(())
1012 }
1013 pub fn apply_t(&mut self, qubit: usize) -> Result<(), QisError> {
1015 self.apply_phase(qubit, FRAC_PI_4)?;
1016 Ok(())
1017 }
1018 pub fn apply_tdg(&mut self, qubit: usize) -> Result<(), QisError> {
1020 self.apply_phase(qubit, -FRAC_PI_4)?;
1021 Ok(())
1022 }
1023 pub fn apply_x2p(&mut self, qubit: usize) -> Result<(), QisError> {
1025 self.apply_rx(qubit, FRAC_PI_2)?;
1026 Ok(())
1027 }
1028 pub fn apply_x2m(&mut self, qubit: usize) -> Result<(), QisError> {
1030 self.apply_rx(qubit, -FRAC_PI_2)?;
1031 Ok(())
1032 }
1033 pub fn apply_y2p(&mut self, qubit: usize) -> Result<(), QisError> {
1035 self.apply_ry(qubit, FRAC_PI_2)?;
1036 Ok(())
1037 }
1038 pub fn apply_y2m(&mut self, qubit: usize) -> Result<(), QisError> {
1040 self.apply_ry(qubit, -FRAC_PI_2)?;
1041 Ok(())
1042 }
1043 pub fn apply_rxy(&mut self, qubit: usize, theta: f64, phi: f64) -> Result<(), QisError> {
1045 self.apply_u(qubit, theta, phi - FRAC_PI_2, FRAC_PI_2 - phi)?;
1046 Ok(())
1047 }
1048 pub fn apply_xy2p(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
1050 self.apply_rxy(qubit, FRAC_PI_2, theta)?;
1051 Ok(())
1052 }
1053 pub fn apply_xy2m(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
1055 self.apply_rxy(qubit, -FRAC_PI_2, theta)?;
1056 Ok(())
1057 }
1058 pub fn apply_xy(&mut self, qubit: usize, theta: f64) -> Result<(), QisError> {
1062 self.validate_qubit(qubit)?;
1063 let mat = StandardGate::XY.matrix(&[theta]).unwrap();
1064 self.apply_single_qubit_gate(
1065 qubit,
1066 [[mat[[0, 0]], mat[[0, 1]]], [mat[[1, 0]], mat[[1, 1]]]],
1067 )?;
1068 Ok(())
1069 }
1070 pub fn apply_gphase(&mut self, _phi: f64) { }
1073 fn apply_p_kernel(&mut self, q: usize, off: usize, t: f64) {
1074 let d = 1 << (q + off);
1075 let phase = Complex64::from_polar(1.0, t);
1076 let kernel = |chunk: &mut [Complex64]| {
1077 u_slice(chunk, d).iter_mut().for_each(|v| *v *= phase);
1078 };
1079 with_maybe_par!(self.num_qubits, self.data, 2 * d, kernel);
1080 }
1081
1082 pub fn apply_cx(&mut self, control: usize, target: usize) -> Result<(), QisError> {
1084 self.validate_two_qubits(control, target)?;
1085 self.apply_cx_kernel(control, target, self.num_qubits);
1086 self.apply_cx_kernel(control, target, 0);
1087 Ok(())
1088 }
1089 pub fn apply_cz(&mut self, q0: usize, q1: usize) -> Result<(), QisError> {
1091 self.validate_two_qubits(q0, q1)?;
1092 self.apply_cz_kernel(q0, q1, self.num_qubits);
1093 self.apply_cz_kernel(q0, q1, 0);
1094 Ok(())
1095 }
1096
1097 pub fn apply_single_qubit_gate(
1099 &mut self,
1100 qubit: usize,
1101 matrix: [[Complex64; 2]; 2],
1102 ) -> Result<(), QisError> {
1103 self.validate_qubit(qubit)?;
1104 let flat = vec![matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1]];
1105 self.apply_matrix_kernel(&[qubit], self.num_qubits, &flat, false);
1106 self.apply_matrix_kernel(&[qubit], 0, &flat, true);
1107 Ok(())
1108 }
1109
1110 pub fn apply_two_qubit_gate(
1112 &mut self,
1113 q0: usize,
1114 q1: usize,
1115 matrix: [[Complex64; 4]; 4],
1116 ) -> Result<(), QisError> {
1117 self.validate_two_qubits(q0, q1)?;
1118 let mut flat = Vec::with_capacity(16);
1119 for row in &matrix {
1120 for &val in row {
1121 flat.push(val);
1122 }
1123 }
1124 self.apply_matrix_kernel(&[q0, q1], self.num_qubits, &flat, false);
1125 self.apply_matrix_kernel(&[q0, q1], 0, &flat, true);
1126 Ok(())
1127 }
1128
1129 pub fn apply_swap(&mut self, q0: usize, q1: usize) -> Result<(), QisError> {
1131 self.validate_two_qubits(q0, q1)?;
1132 let mat = StandardGate::SWAP.matrix(&[]).unwrap();
1133 self.apply_unitary_gate(&[q0, q1], &mat)?;
1134 Ok(())
1135 }
1136
1137 pub fn apply_cy(&mut self, control: usize, target: usize) -> Result<(), QisError> {
1139 self.validate_two_qubits(control, target)?;
1140 let mat = StandardGate::CY.matrix(&[]).unwrap();
1141 self.apply_unitary_gate(&[control, target], &mat)?;
1142 Ok(())
1143 }
1144
1145 pub fn apply_ccx(&mut self, c0: usize, c1: usize, target: usize) -> Result<(), QisError> {
1147 self.validate_qubits(&[c0, c1, target])?;
1148 let mat = StandardGate::CCX.matrix(&[]).unwrap();
1149 self.apply_unitary_gate(&[c0, c1, target], &mat)?;
1150 Ok(())
1151 }
1152
1153 pub fn apply_rxx(&mut self, q0: usize, q1: usize, theta: f64) -> Result<(), QisError> {
1155 self.validate_two_qubits(q0, q1)?;
1156 let mat = StandardGate::RXX.matrix(&[theta]).unwrap();
1157 self.apply_unitary_gate(&[q0, q1], &mat)?;
1158 Ok(())
1159 }
1160
1161 pub fn apply_ryy(&mut self, q0: usize, q1: usize, theta: f64) -> Result<(), QisError> {
1163 self.validate_two_qubits(q0, q1)?;
1164 let mat = StandardGate::RYY.matrix(&[theta]).unwrap();
1165 self.apply_unitary_gate(&[q0, q1], &mat)?;
1166 Ok(())
1167 }
1168
1169 pub fn apply_rzz(&mut self, q0: usize, q1: usize, theta: f64) -> Result<(), QisError> {
1171 self.validate_two_qubits(q0, q1)?;
1172 let mat = StandardGate::RZZ.matrix(&[theta]).unwrap();
1173 self.apply_unitary_gate(&[q0, q1], &mat)?;
1174 Ok(())
1175 }
1176
1177 pub fn apply_rzx(&mut self, q0: usize, q1: usize, theta: f64) -> Result<(), QisError> {
1179 self.validate_two_qubits(q0, q1)?;
1180 let mat = StandardGate::RZX.matrix(&[theta]).unwrap();
1181 self.apply_unitary_gate(&[q0, q1], &mat)?;
1182 Ok(())
1183 }
1184
1185 pub fn apply_crx(&mut self, control: usize, target: usize, theta: f64) -> Result<(), QisError> {
1187 self.validate_two_qubits(control, target)?;
1188 let mat = StandardGate::CRX.matrix(&[theta]).unwrap();
1189 self.apply_unitary_gate(&[control, target], &mat)?;
1190 Ok(())
1191 }
1192
1193 pub fn apply_cry(&mut self, control: usize, target: usize, theta: f64) -> Result<(), QisError> {
1195 self.validate_two_qubits(control, target)?;
1196 let mat = StandardGate::CRY.matrix(&[theta]).unwrap();
1197 self.apply_unitary_gate(&[control, target], &mat)?;
1198 Ok(())
1199 }
1200
1201 pub fn apply_crz(&mut self, control: usize, target: usize, theta: f64) -> Result<(), QisError> {
1203 self.validate_two_qubits(control, target)?;
1204 let mat = StandardGate::CRZ.matrix(&[theta]).unwrap();
1205 self.apply_unitary_gate(&[control, target], &mat)?;
1206 Ok(())
1207 }
1208
1209 pub fn apply_fsim(
1211 &mut self,
1212 q0: usize,
1213 q1: usize,
1214 theta: f64,
1215 phi: f64,
1216 ) -> Result<(), QisError> {
1217 self.validate_two_qubits(q0, q1)?;
1218 let mat = StandardGate::FSIM.matrix(&[theta, phi]).unwrap();
1219 self.apply_unitary_gate(&[q0, q1], &mat)?;
1220 Ok(())
1221 }
1222
1223 pub fn apply_unitary_gate(
1231 &mut self,
1232 qubits: &[usize],
1233 matrix: &ndarray::Array2<Complex64>,
1234 ) -> Result<(), QisError> {
1235 let num_target_qubits = qubits.len();
1236 let gate_dim = 1 << num_target_qubits;
1237
1238 if num_target_qubits == 0 || num_target_qubits > self.num_qubits {
1240 return Err(QisError::InvalidParameterValue(format!(
1241 "Invalid number of target qubits: {} (must be 1 to {})",
1242 num_target_qubits, self.num_qubits
1243 )));
1244 }
1245
1246 for (i, &q) in qubits.iter().enumerate() {
1248 if q >= self.num_qubits {
1249 return Err(QisError::IndexOutOfBounds {
1250 index: q,
1251 max: self.num_qubits.saturating_sub(1),
1252 });
1253 }
1254 for &other in &qubits[..i] {
1255 if q == other {
1256 return Err(QisError::InvalidParameterValue(format!(
1257 "Duplicate qubit index {} in apply_unitary_gate",
1258 q
1259 )));
1260 }
1261 }
1262 }
1263
1264 if matrix.shape() != [gate_dim, gate_dim] {
1266 return Err(QisError::InvalidParameterValue(format!(
1267 "Matrix dimensions {}x{} don't match expected {}x{} for {} qubits",
1268 matrix.nrows(),
1269 matrix.ncols(),
1270 gate_dim,
1271 gate_dim,
1272 num_target_qubits
1273 )));
1274 }
1275
1276 let flat: Vec<Complex64> = matrix.iter().cloned().collect();
1277 self.apply_matrix_kernel(qubits, self.num_qubits, &flat, false);
1278 self.apply_matrix_kernel(qubits, 0, &flat, true);
1279 Ok(())
1280 }
1281
1282 fn apply_matrix_kernel(&mut self, qs: &[usize], off: usize, mat: &[Complex64], conj: bool) {
1283 Self::apply_matrix_kernel_impl(&mut self.data, self.num_qubits, qs, off, mat, conj);
1284 }
1285
1286 fn apply_matrix_kernel_impl(
1287 data: &mut [Complex64],
1288 num_qubits: usize,
1289 qs: &[usize],
1290 off: usize,
1291 mat: &[Complex64],
1292 conj: bool,
1293 ) {
1294 if qs.is_empty() {
1295 return;
1296 }
1297 let n = qs.len();
1298 let dim = 1 << n;
1299 let mut offsets: SmallVec<[usize; 16]> = smallvec![0; dim];
1300 for i in 0..dim {
1301 for (j, &q) in qs.iter().enumerate() {
1302 if (i >> (n - 1 - j)) & 1 == 1 {
1303 offsets[i] |= 1 << (q + off);
1304 }
1305 }
1306 }
1307 let mut sorted: SmallVec<[usize; 16]> = qs.iter().map(|&q| q + off).collect();
1308 sorted.sort_unstable();
1309 let chunk_size = 1 << (sorted.last().unwrap() + 1);
1310
1311 let kernel = |chunk: &mut [Complex64]| {
1312 let mut i_buf: SmallVec<[Complex64; 16]> = smallvec![Complex64::default(); dim];
1313 let mut o_buf: SmallVec<[Complex64; 16]> = smallvec![Complex64::default(); dim];
1314 for b_idx in 0..(chunk.len() >> n) {
1315 let mut base = b_idx;
1316 for &q in &sorted {
1317 let mask = (1 << q) - 1;
1318 base = ((base & !mask) << 1) | (base & mask);
1319 }
1320 for i in 0..dim {
1321 i_buf[i] = chunk[base + offsets[i]];
1322 }
1323 for r in 0..dim {
1324 let mut sum = Complex64::default();
1325 for c in 0..dim {
1326 let val = mat[r * dim + c];
1327 let coeff = if conj { val.conj() } else { val };
1328 sum += coeff * i_buf[c];
1329 }
1330 o_buf[r] = sum;
1331 }
1332 for i in 0..dim {
1333 chunk[base + offsets[i]] = o_buf[i];
1334 }
1335 }
1336 };
1337 with_maybe_par!(num_qubits, data, chunk_size, kernel);
1338 }
1339
1340 pub fn apply_kraus(&mut self, ops: &[Vec<Complex64>], qs: &[usize]) -> Result<(), QisError> {
1349 if ops.is_empty() {
1350 return Err(QisError::InvalidParameterValue(
1351 "Kraus operator list must not be empty".to_string(),
1352 ));
1353 }
1354 if qs.is_empty() {
1355 return Err(QisError::InvalidParameterValue(
1356 "Kraus channel must target at least one qubit".to_string(),
1357 ));
1358 }
1359 self.validate_qubits(qs)?;
1360 let op_dim = 1usize << qs.len();
1361 let expected_len = op_dim * op_dim;
1362 for (idx, op) in ops.iter().enumerate() {
1363 if op.len() != expected_len {
1364 return Err(QisError::InvalidParameterValue(format!(
1365 "Kraus operator {} has length {}, expected {} for {} target qubits",
1366 idx,
1367 op.len(),
1368 expected_len,
1369 qs.len()
1370 )));
1371 }
1372 }
1373 let source_data = self.data.clone();
1374
1375 for val in self.data.iter_mut() {
1376 *val = Complex64::default();
1377 }
1378
1379 let mut work_buffer = vec![Complex64::default(); self.data.len()];
1380
1381 for op in ops {
1382 work_buffer.copy_from_slice(&source_data);
1383
1384 Self::apply_matrix_kernel_impl(
1385 &mut work_buffer,
1386 self.num_qubits,
1387 qs,
1388 self.num_qubits,
1389 op,
1390 false,
1391 );
1392 Self::apply_matrix_kernel_impl(&mut work_buffer, self.num_qubits, qs, 0, op, true);
1393
1394 if self.num_qubits < PARALLEL_THRESHOLD {
1395 for (acc, val) in self.data.iter_mut().zip(&work_buffer) {
1396 *acc += val;
1397 }
1398 } else {
1399 use rayon::prelude::*;
1400 self.data
1401 .par_iter_mut()
1402 .zip(work_buffer.par_iter())
1403 .for_each(|(acc, val)| {
1404 *acc += val;
1405 });
1406 }
1407 }
1408
1409 #[cfg(debug_assertions)]
1410 {
1411 let tr = self.trace();
1412 debug_assert!((tr.re - 1.0).abs() < 1e-10, "Trace error: {}", tr);
1413 }
1414 Ok(())
1415 }
1416
1417 pub fn partial_trace(&self, keep: &[usize]) -> Result<Self, QisError> {
1428 for (idx, &q) in keep.iter().enumerate() {
1429 if q >= self.num_qubits {
1430 return Err(QisError::IndexOutOfBounds {
1431 index: q,
1432 max: self.num_qubits.saturating_sub(1),
1433 });
1434 }
1435 if keep[..idx].contains(&q) {
1436 return Err(QisError::InvalidParameterValue(format!(
1437 "Duplicate qubit index {} in partial_trace",
1438 q
1439 )));
1440 }
1441 }
1442 let mut s_keep = keep.to_vec();
1443 s_keep.sort_unstable();
1444 let all: HashSet<_> = (0..self.num_qubits).collect();
1445 let mut trace: Vec<_> = all
1446 .difference(&s_keep.iter().cloned().collect())
1447 .cloned()
1448 .collect();
1449 trace.sort_unstable();
1450 let (n_k, n_t) = (s_keep.len(), trace.len());
1451 let (dim, r_dim) = (1 << self.num_qubits, 1 << n_k);
1452 let mut res = Self::zeros(n_k);
1453 res.qubit_map = self.qubit_map.as_ref().map(|qubit_map| {
1454 std::sync::Arc::new(
1455 qubit_map
1456 .iter()
1457 .filter_map(|(&qubit, position)| {
1458 s_keep
1459 .binary_search(position)
1460 .ok()
1461 .map(|index| (qubit, index))
1462 })
1463 .collect(),
1464 )
1465 });
1466 res.data.par_iter_mut().enumerate().for_each(|(idx, val)| {
1467 let (i, j) = (idx / r_dim, idx % r_dim);
1468 let mut sum = Complex64::default();
1469 for t in 0..(1 << n_t) {
1470 sum += self.data[interleave_bits(&s_keep, i, &trace, t) * dim
1471 + interleave_bits(&s_keep, j, &trace, t)];
1472 }
1473 *val = sum;
1474 });
1475 Ok(res)
1476 }
1477
1478 pub(crate) fn partial_transpose(&self, target_qubits: &[usize]) -> Result<Self, QisError> {
1479 for &q in target_qubits {
1480 self.validate_qubit(q)?;
1481 }
1482
1483 let n = self.num_qubits;
1484 let mut swap_mask = 0usize;
1485 for &q in target_qubits {
1486 swap_mask |= 1 << q;
1487 }
1488
1489 let mut res = Self::zeros(n);
1490 res.qubit_map.clone_from(&self.qubit_map);
1491 res.data.par_iter_mut().enumerate().for_each(|(idx, val)| {
1492 let lower_swap_bits = idx & swap_mask;
1493 let upper_swap_bits = (idx >> n) & swap_mask;
1494
1495 let mut src_idx = idx & !(swap_mask | (swap_mask << n));
1496 src_idx |= upper_swap_bits;
1497 src_idx |= lower_swap_bits << n;
1498
1499 *val = self.data[src_idx];
1500 });
1501
1502 Ok(res)
1503 }
1504
1505 pub fn expectation(&self, h: &dyn Observable) -> Result<f64, QisError> {
1521 h.expectation_density_matrix(self)
1522 }
1523
1524 pub(crate) fn reset_from(&mut self, source: &DensityMatrix) {
1529 debug_assert_eq!(self.num_qubits, source.num_qubits);
1530 self.qubit_map.clone_from(&source.qubit_map);
1531 self.data.copy_from_slice(&source.data);
1532 }
1533
1534 pub fn measure(&mut self, qubit: usize) -> Result<bool, QisError> {
1540 self.measure_with_rng(qubit, &mut rand::rng())
1541 }
1542
1543 pub fn measure_with_rng(&mut self, qubit: usize, rng: &mut impl Rng) -> Result<bool, QisError> {
1545 self.validate_qubit(qubit)?;
1546 let dim = 1 << self.num_qubits;
1547 let mask = 1usize << qubit;
1548
1549 let p1: f64 = (0..dim)
1551 .filter(|i| i & mask != 0)
1552 .map(|i| self.data[i * dim + i].re)
1553 .sum();
1554
1555 let outcome = rng.random::<f64>() < p1;
1556
1557 let norm = if outcome { p1 } else { 1.0 - p1 };
1559 for row in 0..dim {
1560 let row_match = if outcome {
1561 (row & mask) != 0
1562 } else {
1563 (row & mask) == 0
1564 };
1565 for col in 0..dim {
1566 let col_match = if outcome {
1567 (col & mask) != 0
1568 } else {
1569 (col & mask) == 0
1570 };
1571 if row_match && col_match {
1572 self.data[row * dim + col] /= norm;
1573 } else {
1574 self.data[row * dim + col] = Complex64::new(0.0, 0.0);
1575 }
1576 }
1577 }
1578
1579 Ok(outcome)
1580 }
1581
1582 pub fn reset(&mut self, qubit: usize) -> Result<(), QisError> {
1588 self.validate_qubit(qubit)?;
1589 let dim = 1usize << self.num_qubits;
1590 let mask = 1usize << qubit;
1591 for row in (0..dim).filter(|row| row & mask == 0) {
1592 for col in (0..dim).filter(|col| col & mask == 0) {
1593 let i00 = row * dim + col;
1594 let i01 = row * dim + (col | mask);
1595 let i10 = (row | mask) * dim + col;
1596 let i11 = (row | mask) * dim + (col | mask);
1597 let sum = self.data[i00] + self.data[i11];
1598 self.data[i00] = sum;
1599 self.data[i01] = Complex64::new(0.0, 0.0);
1600 self.data[i10] = Complex64::new(0.0, 0.0);
1601 self.data[i11] = Complex64::new(0.0, 0.0);
1602 }
1603 }
1604 Ok(())
1605 }
1606
1607 pub fn measure_all(&mut self) -> Outcome {
1613 self.measure_all_with_rng(&mut rand::rng())
1614 }
1615
1616 pub fn measure_all_with_rng(&mut self, rng: &mut impl Rng) -> Outcome {
1618 let num_chunks = self.num_qubits.div_ceil(64);
1619 let mut chunks = SmallVec::from_elem(0u64, num_chunks);
1620 for q in 0..self.num_qubits {
1621 if self.measure_with_rng(q, rng).unwrap() {
1622 chunks[q / 64] |= 1u64 << (q % 64);
1623 }
1624 }
1625 Outcome::new(chunks)
1626 }
1627
1628 pub fn sample_shots(&self, shots: usize) -> Vec<Outcome> {
1647 self.sample_shots_with_seed(shots, None)
1648 }
1649
1650 pub fn sample_shots_with_seed(&self, shots: usize, seed: Option<u64>) -> Vec<Outcome> {
1653 if let Some(seed) = seed {
1654 let mut rng = SmallRng::seed_from_u64(seed);
1655 let seeds: Vec<u64> = (0..shots).map(|_| rng.random()).collect();
1656 return seeds
1657 .into_par_iter()
1658 .map_with(self.clone(), |work, seed| {
1659 work.reset_from(self);
1660 work.measure_all_with_rng(&mut SmallRng::seed_from_u64(seed))
1661 })
1662 .collect();
1663 }
1664 (0..shots)
1665 .into_par_iter()
1666 .map_with(self.clone(), |work, _| {
1667 work.reset_from(self);
1668 work.measure_all()
1669 })
1670 .collect()
1671 }
1672
1673 pub fn sample(
1679 &self,
1680 measurement: &Measurement,
1681 shots: usize,
1682 ) -> Result<ExecutionResult, QisError> {
1683 self.sample_with_seed(measurement, shots, None)
1684 }
1685
1686 pub fn sample_with_seed(
1688 &self,
1689 measurement: &Measurement,
1690 shots: usize,
1691 seed: Option<u64>,
1692 ) -> Result<ExecutionResult, QisError> {
1693 let projection = super::resolve_measurement(measurement, &self.qubit_map, self.num_qubits)?;
1694
1695 let mut counts = HashMap::new();
1696 for full in self.sample_shots_with_seed(shots, seed) {
1697 *counts.entry(projection.project(&full)).or_insert(0usize) += 1;
1698 }
1699
1700 Ok(ExecutionResult::from_counts(
1701 "density-matrix-sample".to_string(),
1702 measurement.qubits().to_vec(),
1703 shots,
1704 measurement.width(),
1705 Some("density-matrix".to_string()),
1706 counts,
1707 ))
1708 }
1709
1710 pub fn probs(&self, measurement: &Measurement) -> Result<HashMap<Outcome, f64>, QisError> {
1716 let projection = super::resolve_measurement(measurement, &self.qubit_map, self.num_qubits)?;
1717
1718 let mut marginal = HashMap::new();
1719 for (basis, prob) in self.probabilities().into_iter().enumerate() {
1720 if prob == 0.0 {
1721 continue;
1722 }
1723 *marginal
1724 .entry(projection.project_basis(basis))
1725 .or_insert(0.0) += prob;
1726 }
1727 Ok(marginal)
1728 }
1729}
1730
1731#[inline(always)]
1733fn u_slice(s: &mut [Complex64], d: usize) -> &mut [Complex64] {
1734 s.split_at_mut(d).1
1735}
1736
1737#[cfg(test)]
1738#[path = "density_matrix_test.rs"]
1739mod density_matrix_test;