1use crate::Result;
6use scirs2_core::ndarray::{s, Array1, Array2, Axis};
7use scirs2_core::Complex64;
8
9use super::types::{
10 ClassicalShadow, MeasurementData, QuantumChannel, QuantumInfoError, QuantumState,
11};
12
13pub fn state_fidelity(
25 state1: &QuantumState,
26 state2: &QuantumState,
27) -> std::result::Result<f64, QuantumInfoError> {
28 match (state1, state2) {
29 (QuantumState::Pure(psi), QuantumState::Pure(phi)) => {
30 let inner = inner_product(psi, phi);
31 Ok(inner.norm_sqr())
32 }
33 (QuantumState::Pure(psi), QuantumState::Mixed(rho)) => {
34 let psi_dag = psi.mapv(|c| c.conj());
35 let rho_psi = rho.dot(psi);
36 let fid = inner_product(&psi_dag, &rho_psi);
37 Ok(fid.re.max(0.0))
38 }
39 (QuantumState::Mixed(rho), QuantumState::Pure(psi)) => {
40 let psi_dag = psi.mapv(|c| c.conj());
41 let rho_psi = rho.dot(psi);
42 let fid = inner_product(&psi_dag, &rho_psi);
43 Ok(fid.re.max(0.0))
44 }
45 (QuantumState::Mixed(rho1), QuantumState::Mixed(rho2)) => mixed_state_fidelity(rho1, rho2),
46 }
47}
48fn inner_product(psi: &Array1<Complex64>, phi: &Array1<Complex64>) -> Complex64 {
50 psi.iter().zip(phi.iter()).map(|(a, b)| a.conj() * b).sum()
51}
52fn mixed_state_fidelity(
54 rho1: &Array2<Complex64>,
55 rho2: &Array2<Complex64>,
56) -> std::result::Result<f64, QuantumInfoError> {
57 let n = rho1.nrows();
58 if n != rho1.ncols() || n != rho2.nrows() || n != rho2.ncols() {
59 return Err(QuantumInfoError::DimensionMismatch(
60 "Density matrices must be square and have the same dimensions".to_string(),
61 ));
62 }
63 let mut fid_sum = Complex64::new(0.0, 0.0);
64 for i in 0..n {
65 for j in 0..n {
66 fid_sum += rho1[[i, j]].conj() * rho2[[i, j]];
67 }
68 }
69 let fid = fid_sum.re.sqrt().powi(2).max(0.0).min(1.0);
70 Ok(fid)
71}
72pub fn purity(state: &QuantumState) -> std::result::Result<f64, QuantumInfoError> {
84 match state {
85 QuantumState::Pure(_) => Ok(1.0),
86 QuantumState::Mixed(rho) => {
87 let rho_squared = rho.dot(rho);
88 let trace: Complex64 = (0..rho.nrows()).map(|i| rho_squared[[i, i]]).sum();
89 Ok(trace.re.max(0.0).min(1.0))
90 }
91 }
92}
93pub fn von_neumann_entropy(
108 state: &QuantumState,
109 base: Option<f64>,
110) -> std::result::Result<f64, QuantumInfoError> {
111 let log_base = base.unwrap_or(2.0);
112 match state {
113 QuantumState::Pure(_) => Ok(0.0),
114 QuantumState::Mixed(rho) => {
115 let eigenvalues = compute_eigenvalues_hermitian(rho)?;
116 let mut entropy = 0.0;
117 for &lambda in &eigenvalues {
118 if lambda > 1e-15 {
119 entropy -= lambda * lambda.log(log_base);
120 }
121 }
122 Ok(entropy.max(0.0))
123 }
124 }
125}
126fn compute_eigenvalues_hermitian(
128 matrix: &Array2<Complex64>,
129) -> std::result::Result<Vec<f64>, QuantumInfoError> {
130 let n = matrix.nrows();
131 let mut eigenvalues = Vec::with_capacity(n);
132 for i in 0..n {
133 let diag = matrix[[i, i]].re;
134 if diag.abs() > 1e-15 {
135 eigenvalues.push(diag);
136 }
137 }
138 let sum: f64 = eigenvalues.iter().sum();
139 if sum > 1e-10 {
140 for e in &mut eigenvalues {
141 *e /= sum;
142 }
143 }
144 Ok(eigenvalues)
145}
146pub fn mutual_information(
158 state: &QuantumState,
159 dims: (usize, usize),
160 base: Option<f64>,
161) -> std::result::Result<f64, QuantumInfoError> {
162 let rho = state.to_density_matrix()?;
163 let (dim_a, dim_b) = dims;
164 if rho.nrows() != dim_a * dim_b {
165 return Err(QuantumInfoError::DimensionMismatch(format!(
166 "State dimension {} doesn't match subsystem dimensions {}×{}",
167 rho.nrows(),
168 dim_a,
169 dim_b
170 )));
171 }
172 let rho_a = partial_trace(&rho, dim_b, false)?;
173 let rho_b = partial_trace(&rho, dim_a, true)?;
174 let s_ab = von_neumann_entropy(&QuantumState::Mixed(rho.clone()), base)?;
175 let s_a = von_neumann_entropy(&QuantumState::Mixed(rho_a), base)?;
176 let s_b = von_neumann_entropy(&QuantumState::Mixed(rho_b), base)?;
177 Ok((s_a + s_b - s_ab).max(0.0))
178}
179pub fn partial_trace(
189 rho: &Array2<Complex64>,
190 dim_traced: usize,
191 trace_first: bool,
192) -> std::result::Result<Array2<Complex64>, QuantumInfoError> {
193 let n = rho.nrows();
194 let dim_kept = n / dim_traced;
195 if dim_kept * dim_traced != n {
196 return Err(QuantumInfoError::DimensionMismatch(format!(
197 "Matrix dimension {} is not divisible by {}",
198 n, dim_traced
199 )));
200 }
201 let mut reduced = Array2::zeros((dim_kept, dim_kept));
202 if trace_first {
203 for i in 0..dim_kept {
204 for j in 0..dim_kept {
205 let mut sum = Complex64::new(0.0, 0.0);
206 for k in 0..dim_traced {
207 sum += rho[[k * dim_kept + i, k * dim_kept + j]];
208 }
209 reduced[[i, j]] = sum;
210 }
211 }
212 } else {
213 for i in 0..dim_kept {
214 for j in 0..dim_kept {
215 let mut sum = Complex64::new(0.0, 0.0);
216 for k in 0..dim_traced {
217 sum += rho[[i * dim_traced + k, j * dim_traced + k]];
218 }
219 reduced[[i, j]] = sum;
220 }
221 }
222 }
223 Ok(reduced)
224}
225pub fn concurrence(state: &QuantumState) -> std::result::Result<f64, QuantumInfoError> {
241 match state {
242 QuantumState::Pure(psi) => {
243 if psi.len() != 4 {
244 return Err(QuantumInfoError::DimensionMismatch(
245 "Concurrence is only defined for 2-qubit states (dimension 4)".to_string(),
246 ));
247 }
248 let alpha = psi[0];
249 let beta = psi[1];
250 let gamma = psi[2];
251 let delta = psi[3];
252 let c = 2.0 * (alpha * delta - beta * gamma).norm();
253 Ok(c.min(1.0))
254 }
255 QuantumState::Mixed(rho) => {
256 if rho.nrows() != 4 {
257 return Err(QuantumInfoError::DimensionMismatch(
258 "Concurrence is only defined for 2-qubit states (dimension 4)".to_string(),
259 ));
260 }
261 let sigma_yy = Array2::from_shape_vec(
262 (4, 4),
263 vec![
264 Complex64::new(0.0, 0.0),
265 Complex64::new(0.0, 0.0),
266 Complex64::new(0.0, 0.0),
267 Complex64::new(-1.0, 0.0),
268 Complex64::new(0.0, 0.0),
269 Complex64::new(0.0, 0.0),
270 Complex64::new(1.0, 0.0),
271 Complex64::new(0.0, 0.0),
272 Complex64::new(0.0, 0.0),
273 Complex64::new(1.0, 0.0),
274 Complex64::new(0.0, 0.0),
275 Complex64::new(0.0, 0.0),
276 Complex64::new(-1.0, 0.0),
277 Complex64::new(0.0, 0.0),
278 Complex64::new(0.0, 0.0),
279 Complex64::new(0.0, 0.0),
280 ],
281 )
282 .map_err(|e| QuantumInfoError::NumericalError(e.to_string()))?;
283 let rho_star = rho.mapv(|c| c.conj());
284 let temp1 = sigma_yy.dot(&rho_star);
285 let rho_tilde = temp1.dot(&sigma_yy);
286 let r_matrix = rho.dot(&rho_tilde);
287 let eigenvalues = compute_4x4_eigenvalues(&r_matrix)?;
288 let mut lambdas: Vec<f64> = eigenvalues.iter().map(|e| e.re.max(0.0).sqrt()).collect();
289 lambdas.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
290 let concurrence = (lambdas[0] - lambdas[1] - lambdas[2] - lambdas[3]).max(0.0);
291 Ok(concurrence.min(1.0))
292 }
293 }
294}
295fn compute_4x4_eigenvalues(
297 matrix: &Array2<Complex64>,
298) -> std::result::Result<Vec<Complex64>, QuantumInfoError> {
299 let n = matrix.nrows();
300 let mut eigenvalues = Vec::with_capacity(n);
301 let trace: Complex64 = (0..n).map(|i| matrix[[i, i]]).sum();
302 for i in 0..n {
303 let row_sum: Complex64 = (0..n)
304 .map(|j| matrix[[i, j]].norm_sqr())
305 .sum::<f64>()
306 .into();
307 eigenvalues.push(Complex64::new(row_sum.re.sqrt() / n as f64, 0.0));
308 }
309 let eigen_sum: Complex64 = eigenvalues.iter().sum();
310 if eigen_sum.norm() > 1e-10 {
311 let scale = trace / eigen_sum;
312 for e in &mut eigenvalues {
313 *e *= scale;
314 }
315 }
316 Ok(eigenvalues)
317}
318pub fn entanglement_of_formation(
329 state: &QuantumState,
330) -> std::result::Result<f64, QuantumInfoError> {
331 let c = concurrence(state)?;
332 if c < 1e-15 {
333 return Ok(0.0);
334 }
335 let x = (1.0 + (1.0 - c * c).max(0.0).sqrt()) / 2.0;
336 let h = if x > 1e-15 && x < 1.0 - 1e-15 {
337 -x * x.log2() - (1.0 - x) * (1.0 - x).log2()
338 } else if x <= 1e-15 {
339 0.0
340 } else {
341 0.0
342 };
343 Ok(h)
344}
345pub fn negativity(
357 state: &QuantumState,
358 dims: (usize, usize),
359) -> std::result::Result<f64, QuantumInfoError> {
360 let rho = state.to_density_matrix()?;
361 let (dim_a, dim_b) = dims;
362 if rho.nrows() != dim_a * dim_b {
363 return Err(QuantumInfoError::DimensionMismatch(format!(
364 "State dimension {} doesn't match subsystem dimensions {}×{}",
365 rho.nrows(),
366 dim_a,
367 dim_b
368 )));
369 }
370 let rho_pt = partial_transpose(&rho, dim_a, dim_b)?;
371 let eigenvalues = compute_eigenvalues_hermitian(&rho_pt)?;
372 let trace_norm: f64 = eigenvalues.iter().map(|e| e.abs()).sum();
373 Ok((trace_norm - 1.0).max(0.0) / 2.0)
374}
375fn partial_transpose(
377 rho: &Array2<Complex64>,
378 dim_a: usize,
379 dim_b: usize,
380) -> std::result::Result<Array2<Complex64>, QuantumInfoError> {
381 let n = dim_a * dim_b;
382 let mut rho_pt = Array2::zeros((n, n));
383 for i in 0..dim_a {
384 for j in 0..dim_a {
385 for k in 0..dim_b {
386 for l in 0..dim_b {
387 rho_pt[[j * dim_b + k, i * dim_b + l]] = rho[[i * dim_b + k, j * dim_b + l]];
388 }
389 }
390 }
391 }
392 Ok(rho_pt)
393}
394pub fn logarithmic_negativity(
405 state: &QuantumState,
406 dims: (usize, usize),
407) -> std::result::Result<f64, QuantumInfoError> {
408 let rho = state.to_density_matrix()?;
409 let (dim_a, dim_b) = dims;
410 let rho_pt = partial_transpose(&rho, dim_a, dim_b)?;
411 let eigenvalues = compute_eigenvalues_hermitian(&rho_pt)?;
412 let trace_norm: f64 = eigenvalues.iter().map(|e| e.abs()).sum();
413 Ok(trace_norm.log2().max(0.0))
414}
415pub fn process_fidelity(
430 channel: &QuantumChannel,
431 target: &QuantumChannel,
432) -> std::result::Result<f64, QuantumInfoError> {
433 let choi1 = channel.to_choi()?;
434 let choi2 = target.to_choi()?;
435 let dim = choi1.nrows();
436 let input_dim = (dim as f64).sqrt() as usize;
437 let rho1 = &choi1 / Complex64::new(input_dim as f64, 0.0);
438 let rho2 = &choi2 / Complex64::new(input_dim as f64, 0.0);
439 state_fidelity(&QuantumState::Mixed(rho1), &QuantumState::Mixed(rho2))
440}
441pub fn average_gate_fidelity(
452 channel: &QuantumChannel,
453 target: Option<&QuantumChannel>,
454) -> std::result::Result<f64, QuantumInfoError> {
455 let dim = channel.input_dim();
456 let f_pro = if let Some(t) = target {
457 process_fidelity(channel, t)?
458 } else {
459 let identity = QuantumChannel::identity(dim);
460 process_fidelity(channel, &identity)?
461 };
462 let d = dim as f64;
463 Ok((d * f_pro + 1.0) / (d + 1.0))
464}
465pub fn gate_error(
476 channel: &QuantumChannel,
477 target: Option<&QuantumChannel>,
478) -> std::result::Result<f64, QuantumInfoError> {
479 Ok(1.0 - average_gate_fidelity(channel, target)?)
480}
481pub fn unitarity(channel: &QuantumChannel) -> std::result::Result<f64, QuantumInfoError> {
493 let dim = channel.input_dim();
494 let ptm = channel.to_ptm()?;
495 let d = dim as f64;
496 let d_sq = d * d;
497 let mut sum_sq = 0.0;
498 for i in 1..ptm.nrows() {
499 for j in 1..ptm.ncols() {
500 sum_sq += ptm[[i, j]].norm_sqr();
501 }
502 }
503 Ok(sum_sq / (d_sq - 1.0))
504}
505pub fn diamond_norm_distance(
518 channel1: &QuantumChannel,
519 channel2: &QuantumChannel,
520) -> std::result::Result<f64, QuantumInfoError> {
521 let choi1 = channel1.to_choi()?;
522 let choi2 = channel2.to_choi()?;
523 let diff = &choi1 - &choi2;
524 let eigenvalues = compute_eigenvalues_hermitian(&diff)?;
525 let trace_norm: f64 = eigenvalues.iter().map(|e| e.abs()).sum();
526 let dim = (choi1.nrows() as f64).sqrt();
527 Ok((dim * trace_norm).min(2.0))
528}
529pub(super) fn generate_pauli_basis(
531 dim: usize,
532) -> std::result::Result<Vec<Array2<Complex64>>, QuantumInfoError> {
533 if dim == 2 {
534 let i = Array2::from_shape_vec(
535 (2, 2),
536 vec![
537 Complex64::new(1.0, 0.0),
538 Complex64::new(0.0, 0.0),
539 Complex64::new(0.0, 0.0),
540 Complex64::new(1.0, 0.0),
541 ],
542 )
543 .map_err(|e| QuantumInfoError::NumericalError(e.to_string()))?;
544 let x = Array2::from_shape_vec(
545 (2, 2),
546 vec![
547 Complex64::new(0.0, 0.0),
548 Complex64::new(1.0, 0.0),
549 Complex64::new(1.0, 0.0),
550 Complex64::new(0.0, 0.0),
551 ],
552 )
553 .map_err(|e| QuantumInfoError::NumericalError(e.to_string()))?;
554 let y = Array2::from_shape_vec(
555 (2, 2),
556 vec![
557 Complex64::new(0.0, 0.0),
558 Complex64::new(0.0, -1.0),
559 Complex64::new(0.0, 1.0),
560 Complex64::new(0.0, 0.0),
561 ],
562 )
563 .map_err(|e| QuantumInfoError::NumericalError(e.to_string()))?;
564 let z = Array2::from_shape_vec(
565 (2, 2),
566 vec![
567 Complex64::new(1.0, 0.0),
568 Complex64::new(0.0, 0.0),
569 Complex64::new(0.0, 0.0),
570 Complex64::new(-1.0, 0.0),
571 ],
572 )
573 .map_err(|e| QuantumInfoError::NumericalError(e.to_string()))?;
574 Ok(vec![i, x, y, z])
575 } else {
576 let mut basis = Vec::with_capacity(dim * dim);
577 for i in 0..dim {
578 for j in 0..dim {
579 let mut mat = Array2::zeros((dim, dim));
580 mat[[i, j]] = Complex64::new(1.0, 0.0);
581 basis.push(mat);
582 }
583 }
584 Ok(basis)
585 }
586}
587pub(super) fn matrix_trace(matrix: &Array2<Complex64>) -> Complex64 {
589 (0..matrix.nrows().min(matrix.ncols()))
590 .map(|i| matrix[[i, i]])
591 .sum()
592}
593pub(super) fn kronecker_product(a: &Array2<Complex64>, b: &Array2<Complex64>) -> Array2<Complex64> {
595 let (m, n) = (a.nrows(), a.ncols());
596 let (p, q) = (b.nrows(), b.ncols());
597 let mut result = Array2::zeros((m * p, n * q));
598 for i in 0..m {
599 for j in 0..n {
600 for k in 0..p {
601 for l in 0..q {
602 result[[i * p + k, j * q + l]] = a[[i, j]] * b[[k, l]];
603 }
604 }
605 }
606 }
607 result
608}
609#[cfg(test)]
610mod tests {
611 use super::*;
612 #[test]
613 fn test_state_fidelity_pure_states() {
614 let psi = Array1::from_vec(vec![
615 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
616 Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
617 ]);
618 let state1 = QuantumState::Pure(psi.clone());
619 let state2 = QuantumState::Pure(psi);
620 let fid = state_fidelity(&state1, &state2).expect("Fidelity calculation should succeed");
621 assert!(
622 (fid - 1.0).abs() < 1e-10,
623 "Fidelity of identical states should be 1"
624 );
625 }
626 #[test]
627 fn test_state_fidelity_orthogonal_states() {
628 let state1 = QuantumState::computational_basis(2, 0);
629 let state2 = QuantumState::computational_basis(2, 1);
630 let fid = state_fidelity(&state1, &state2).expect("Fidelity calculation should succeed");
631 assert!(
632 fid.abs() < 1e-10,
633 "Fidelity of orthogonal states should be 0"
634 );
635 }
636 #[test]
637 fn test_purity_pure_state() {
638 let state = QuantumState::computational_basis(4, 0);
639 let p = purity(&state).expect("Purity calculation should succeed");
640 assert!((p - 1.0).abs() < 1e-10, "Purity of pure state should be 1");
641 }
642 #[test]
643 fn test_purity_maximally_mixed() {
644 let state = QuantumState::maximally_mixed(4);
645 let p = purity(&state).expect("Purity calculation should succeed");
646 assert!(
647 (p - 0.25).abs() < 1e-10,
648 "Purity of maximally mixed state should be 1/d"
649 );
650 }
651 #[test]
652 fn test_von_neumann_entropy_pure_state() {
653 let state = QuantumState::computational_basis(2, 0);
654 let s = von_neumann_entropy(&state, None).expect("Entropy calculation should succeed");
655 assert!(s.abs() < 1e-10, "Entropy of pure state should be 0");
656 }
657 #[test]
658 fn test_von_neumann_entropy_maximally_mixed() {
659 let state = QuantumState::maximally_mixed(4);
660 let s = von_neumann_entropy(&state, None).expect("Entropy calculation should succeed");
661 assert!(
662 (s - 2.0).abs() < 0.5,
663 "Entropy of maximally mixed state should be log₂(d)"
664 );
665 }
666 #[test]
667 fn test_bell_state_creation() {
668 let bell = QuantumState::bell_state(0);
669 let p = purity(&bell).expect("Purity calculation should succeed");
670 assert!((p - 1.0).abs() < 1e-10, "Bell state should be pure");
671 }
672 #[test]
673 fn test_ghz_state_creation() {
674 let ghz = QuantumState::ghz_state(3);
675 assert_eq!(ghz.dim(), 8, "3-qubit GHZ state should have dimension 8");
676 let p = purity(&ghz).expect("Purity calculation should succeed");
677 assert!((p - 1.0).abs() < 1e-10, "GHZ state should be pure");
678 }
679 #[test]
680 fn test_w_state_creation() {
681 let w = QuantumState::w_state(3);
682 assert_eq!(w.dim(), 8, "3-qubit W state should have dimension 8");
683 let p = purity(&w).expect("Purity calculation should succeed");
684 assert!((p - 1.0).abs() < 1e-10, "W state should be pure");
685 }
686 #[test]
687 fn test_partial_trace() {
688 let bell = QuantumState::bell_state(0);
689 let rho = bell
690 .to_density_matrix()
691 .expect("Density matrix conversion should succeed");
692 let rho_a = partial_trace(&rho, 2, false).expect("Partial trace should succeed");
693 assert_eq!(rho_a.nrows(), 2);
694 assert!((rho_a[[0, 0]].re - 0.5).abs() < 1e-10);
695 assert!((rho_a[[1, 1]].re - 0.5).abs() < 1e-10);
696 }
697 #[test]
698 fn test_concurrence_separable_state() {
699 let state = QuantumState::computational_basis(4, 0);
700 let c = concurrence(&state).expect("Concurrence calculation should succeed");
701 assert!(c < 1e-10, "Concurrence of separable state should be 0");
702 }
703 #[test]
704 fn test_concurrence_bell_state() {
705 let bell = QuantumState::bell_state(0);
706 let c = concurrence(&bell).expect("Concurrence calculation should succeed");
707 assert!(
708 (c - 1.0).abs() < 0.1,
709 "Concurrence of Bell state should be ~1"
710 );
711 }
712 #[test]
713 fn test_quantum_channel_identity() {
714 let channel = QuantumChannel::identity(2);
715 let input = QuantumState::computational_basis(2, 0);
716 let output = channel
717 .apply(&input)
718 .expect("Channel application should succeed");
719 let fid = state_fidelity(&input, &output).expect("Fidelity calculation should succeed");
720 assert!(
721 (fid - 1.0).abs() < 1e-10,
722 "Identity channel should preserve state"
723 );
724 }
725 #[test]
726 fn test_quantum_channel_depolarizing() {
727 let channel = QuantumChannel::depolarizing(0.1);
728 let input = QuantumState::computational_basis(2, 0);
729 let output = channel
730 .apply(&input)
731 .expect("Channel application should succeed");
732 let p = purity(&output).expect("Purity calculation should succeed");
733 assert!(p < 1.0, "Depolarizing channel should decrease purity");
734 assert!(p > 0.5, "Low error rate should keep purity relatively high");
735 }
736 #[test]
737 fn test_average_gate_fidelity_identity() {
738 let channel = QuantumChannel::identity(2);
739 let f_avg =
740 average_gate_fidelity(&channel, None).expect("Average gate fidelity should succeed");
741 assert!(
742 (f_avg - 1.0).abs() < 1e-10,
743 "Identity channel should have fidelity 1"
744 );
745 }
746 #[test]
747 fn test_measurement_data_expectation() {
748 let mut counts = std::collections::HashMap::new();
749 counts.insert("0".to_string(), 700);
750 counts.insert("1".to_string(), 300);
751 let data = MeasurementData::new("Z", counts);
752 let exp = data.expectation_value();
753 assert!((exp - 0.4).abs() < 1e-10);
754 }
755 #[test]
756 fn test_classical_shadow_observable_estimation() {
757 let bases = vec!["Z".to_string(), "Z".to_string(), "Z".to_string()];
758 let outcomes = vec!["0".to_string(), "0".to_string(), "1".to_string()];
759 let shadow = ClassicalShadow::from_measurements(1, bases, outcomes)
760 .expect("Shadow creation should succeed");
761 let z_exp = shadow
762 .estimate_observable("Z")
763 .expect("Observable estimation should succeed");
764 assert!((z_exp - 1.0).abs() < 1e-10);
765 }
766 #[test]
767 fn test_kronecker_product() {
768 let a = Array2::from_shape_vec(
769 (2, 2),
770 vec![
771 Complex64::new(1.0, 0.0),
772 Complex64::new(0.0, 0.0),
773 Complex64::new(0.0, 0.0),
774 Complex64::new(1.0, 0.0),
775 ],
776 )
777 .expect("Valid shape");
778 let b = Array2::from_shape_vec(
779 (2, 2),
780 vec![
781 Complex64::new(0.0, 0.0),
782 Complex64::new(1.0, 0.0),
783 Complex64::new(1.0, 0.0),
784 Complex64::new(0.0, 0.0),
785 ],
786 )
787 .expect("Valid shape");
788 let result = kronecker_product(&a, &b);
789 assert_eq!(result.nrows(), 4);
790 assert_eq!(result.ncols(), 4);
791 assert!((result[[0, 1]].re - 1.0).abs() < 1e-10);
792 assert!((result[[1, 0]].re - 1.0).abs() < 1e-10);
793 assert!((result[[2, 3]].re - 1.0).abs() < 1e-10);
794 assert!((result[[3, 2]].re - 1.0).abs() < 1e-10);
795 }
796}