Skip to content

Commit c920d69

Browse files
committed
optimized tests in quditcircuit
1 parent cee34e6 commit c920d69

File tree

1 file changed

+22
-52
lines changed

1 file changed

+22
-52
lines changed

tests/test_quditcircuit.py

Lines changed: 22 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
sys.path.insert(0, modulepath)
1616
import 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):
483487
def 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

Comments
 (0)