51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
|
|
"""cluster.py -- the full 15x15 bigraduated cluster shadow map M_S for a 2-qubit source
|
||
|
|
cluster S (within 4 qubits), and helpers for building the Pauli tensor of a general
|
||
|
|
(possibly mixed) density matrix."""
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
I2 = np.eye(2, dtype=complex)
|
||
|
|
X = np.array([[0, 1], [1, 0]], dtype=complex)
|
||
|
|
Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
|
||
|
|
Z = np.array([[1, 0], [0, -1]], dtype=complex)
|
||
|
|
_PAULI = [I2, X, Y, Z]
|
||
|
|
|
||
|
|
|
||
|
|
def cluster_map(C, s0, s1):
|
||
|
|
"""C: (4,4,4,4) Pauli correlation tensor. S = {s0,s1} (source cluster), complement =
|
||
|
|
the other two qubits. Returns the normalized 15x15 matrix M_S(rho), normalization
|
||
|
|
1/sqrt((d_S-1)(d_Sc-1)) = 1/sqrt(3*3) = 1/3 for two-qubit clusters in 4 qubits."""
|
||
|
|
others = [k for k in range(4) if k not in (s0, s1)]
|
||
|
|
c0, c1 = others
|
||
|
|
rows = [(i, j) for i in range(4) for j in range(4) if (i, j) != (0, 0)]
|
||
|
|
cols = [(i, j) for i in range(4) for j in range(4) if (i, j) != (0, 0)]
|
||
|
|
M = np.zeros((15, 15))
|
||
|
|
for ri, (ia, ib) in enumerate(rows):
|
||
|
|
for ci, (ic, idd) in enumerate(cols):
|
||
|
|
idx = [0, 0, 0, 0]
|
||
|
|
idx[s0] = ia
|
||
|
|
idx[s1] = ib
|
||
|
|
idx[c0] = ic
|
||
|
|
idx[c1] = idd
|
||
|
|
M[ri, ci] = C[tuple(idx)]
|
||
|
|
return M / 3.0
|
||
|
|
|
||
|
|
|
||
|
|
def nuc(M):
|
||
|
|
return np.linalg.svd(M, compute_uv=False).sum()
|
||
|
|
|
||
|
|
|
||
|
|
def full_tensor_mixed(rho):
|
||
|
|
"""Pauli-tensor extraction for a general (possibly mixed) 16x16 density matrix rho,
|
||
|
|
via direct trace. Slower than core2.full_tensor (which needs a pure-state vector)
|
||
|
|
but works for explicit mixtures (e.g. the Smolin state)."""
|
||
|
|
def kron4(a, b, c, d):
|
||
|
|
return np.kron(np.kron(a, b), np.kron(c, d))
|
||
|
|
C = np.zeros((4, 4, 4, 4))
|
||
|
|
for i0 in range(4):
|
||
|
|
for i1 in range(4):
|
||
|
|
for i2 in range(4):
|
||
|
|
for i3 in range(4):
|
||
|
|
op = kron4(_PAULI[i0], _PAULI[i1], _PAULI[i2], _PAULI[i3])
|
||
|
|
C[i0, i1, i2, i3] = np.real(np.trace(rho @ op))
|
||
|
|
return C
|