1414
1515sys .path .insert (0 , modulepath )
1616import tensorcircuit as tc
17+ import tensorcircuit .quantum as qu
1718
1819
1920@pytest .mark .parametrize ("backend" , [lf ("npb" ), lf ("cpb" )])
@@ -457,24 +458,27 @@ def test_qudit_entanglement_measures_maximally_entangled(backend):
457458 :math:`\rho_A = \operatorname{Tr}_B(\lvert \Phi_d \rangle\langle \Phi_d \rvert) = \mathbb{I}_d/d`.
458459 We check:
459460 - eigenvalues of :math:`\rho_A` are all :math:`1/d`;
460- - purity :math:`\operatorname{Tr}(\rho_A^2) = 1/d`;
461+ - purity :math:`\operatorname{Tr}(\rho_A^2) = 1/d` (via Rényi entropy) ;
461462 - linear entropy :math:`S_L = 1 - \operatorname{Tr}(\rho_A^2) = 1 - 1/d`.
462463 """
463- d = 3 # any d>2 is fine; use qutrit here
464+ d = 3
464465 c = tc .QuditCircuit (2 , d )
465466 c .h (0 )
466467 c .csum (0 , 1 )
467468
468- # |\psi> as a d*d amplitude matrix: \psi[j_A, j_B]
469- psi = tc .backend .numpy (c .state ()).reshape (d , d )
470- rho_A = psi @ psi .conj ().T # partial trace over B
469+ # Reduced density matrix of subsystem A (trace out qudit-1)
470+ rho_A = qu .reduced_density_matrix (c .state (), cut = [1 ], dim = d )
471471
472- evals = np .linalg .eigvalsh (rho_A )
472+ # Spectrum check
473+ evals = tc .backend .eigh (rho_A )[0 ]
473474 np .testing .assert_allclose (evals , np .ones (d ) / d , rtol = 1e-6 , atol = 1e-7 )
474475
475- purity = np .real_if_close (np .trace (rho_A @ rho_A ))
476+ # Purity from Rényi entropy of order 2
477+ S2 = qu .renyi_entropy (rho_A , k = 2 )
478+ purity = float (np .exp (- S2 ))
476479 np .testing .assert_allclose (purity , 1.0 / d , rtol = 1e-6 , atol = 1e-7 )
477480
481+ # Linear entropy check
478482 linear_entropy = 1.0 - purity
479483 np .testing .assert_allclose (linear_entropy , 1.0 - 1.0 / d , rtol = 1e-6 , atol = 1e-7 )
480484
@@ -483,8 +487,7 @@ def test_qudit_entanglement_measures_maximally_entangled(backend):
483487def test_qudit_mutual_information_product_vs_entangled (backend ):
484488 r"""
485489 Compare quantum mutual information :math:`I(A\!:\!B) = S(\rho_A)+S(\rho_B)-S(\rho_{AB})`
486- (with von Neumann entropy :math:`S(\rho)=-\operatorname{Tr}[\rho \ln \rho]`, natural log)
487- for two two-qudit states (local dimension :math:`d>2`):
490+ (with von Neumann entropy from built-in functions) for two two-qudit states (local dimension :math:`d>2`):
488491
489492 1) **Product state** prepared *with gates* by applying single-qudit rotations
490493 :math:`\mathrm{RY}(\theta)` in the two-level subspace :math:`(j,k)=(0,1)` on **each** qudit.
@@ -497,54 +500,21 @@ def test_qudit_mutual_information_product_vs_entangled(backend):
497500 """
498501 d = 3
499502
500- # --- Case 1: explicit product state via local rotations (uses native ry) ---
503+ # Case 1: Product state
501504 c1 = tc .QuditCircuit (2 , d )
502- # rotate each qudit in subspace (0,1) by different angles to ensure it's not trivial
503505 c1 .ry (0 , theta = 0.37 , j = 0 , k = 1 )
504506 c1 .ry (1 , theta = - 0.59 , j = 0 , k = 1 )
505- psi1 = tc .backend .numpy (c1 .state ()).reshape (d , d )
507+ I_AB_1 = qu .mutual_information (c1 .state (), cut = [1 ], dim = d )
508+ np .testing .assert_allclose (I_AB_1 , 0.0 , atol = 1e-7 )
506509
507- # --- Case 2: maximally entangled |\phi_d> via H + CSUM ---
510+ # Case 2: Maximally entangled state
508511 c2 = tc .QuditCircuit (2 , d )
509512 c2 .h (0 )
510513 c2 .csum (0 , 1 )
511- psi2 = tc .backend .numpy (c2 .state ()).reshape (d , d )
512-
513- def reduced_density (psi , trace_out = "B" ):
514- # \rho_A = \psi \psi^\dagger ; \rho_B = \psi^T \psi^*
515- return psi @ psi .conj ().T if trace_out == "B" else psi .T @ psi .conj ()
516-
517- def von_neumann_entropy (rho ):
518- # Hermitize for stability, drop ~0 eigenvalues before log.
519- rh = (rho + rho .conj ().T ) / 2
520- vals = np .linalg .eigvalsh (rh )
521- vals = np .clip (np .real_if_close (vals ), 0.0 , 1.0 )
522- nz = vals > 1e-12
523- return float (- np .sum (vals [nz ] * np .log (vals [nz ])))
524-
525- # product state mutual information
526- rhoA1 , rhoB1 = reduced_density (psi1 , "B" ), reduced_density (psi1 , "A" )
527- rhoAB1 = np .outer (psi1 .reshape (- 1 ), psi1 .conj ().reshape (- 1 )).reshape (d * d , d * d )
528- I_AB_1 = (
529- von_neumann_entropy (rhoA1 )
530- + von_neumann_entropy (rhoB1 )
531- - von_neumann_entropy (rhoAB1 )
532- )
533- np .testing .assert_allclose (I_AB_1 , 0.0 , atol = 1e-7 )
534-
535- # maximally entangled mutual information
536- rhoA2 , rhoB2 = reduced_density (psi2 , "B" ), reduced_density (psi2 , "A" )
537- rhoAB2 = np .outer (psi2 .reshape (- 1 ), psi2 .conj ().reshape (- 1 )).reshape (d * d , d * d )
538- I_AB_2 = (
539- von_neumann_entropy (rhoA2 )
540- + von_neumann_entropy (rhoB2 )
541- - von_neumann_entropy (rhoAB2 )
542- )
543- np .testing .assert_allclose (von_neumann_entropy (rhoAB2 ), 0.0 , atol = 1e-7 )
544- np .testing .assert_allclose (
545- von_neumann_entropy (rhoA2 ), np .log (d ), rtol = 1e-6 , atol = 1e-7
546- )
547- np .testing .assert_allclose (
548- von_neumann_entropy (rhoB2 ), np .log (d ), rtol = 1e-6 , atol = 1e-7
549- )
514+ I_AB_2 = qu .mutual_information (c2 .state (), cut = [1 ], dim = d )
550515 np .testing .assert_allclose (I_AB_2 , 2.0 * np .log (d ), rtol = 1e-6 , atol = 1e-7 )
516+
517+ # Optional: confirm single-subsystem entropy equals log(d)
518+ rho_A = qu .reduced_density_matrix (c2 .state (), cut = [1 ], dim = d )
519+ SA = qu .entropy (rho_A )
520+ np .testing .assert_allclose (SA , np .log (d ), rtol = 1e-6 , atol = 1e-7 )
0 commit comments