The purpose of this notebook is to show how to use entanglish to calculate the entanglement of a pure state.
Given a bipartite density matrix $\rho_{\rvx, \rvy} = \ket{\psi_{\rvx, \rvy}}\bra{\psi_{\rvx, \rvy}}$ with partial trace $\rho_{\rvx}={\rm tr}_\rvy \rho_{\rvx, \rvy}$, we define its entanglement as $S(\rho_{\rvx})$.
First change your working directory to the entanglish directory in your computer, and add its path to the path environment variable.
import os
import sys
print(os.getcwd())
os.chdir('../../')
print(os.getcwd())
sys.path.insert(0,os.getcwd())
/home/rrtucci/PycharmProjects/Entanglish/entanglish/jupyter_notebooks /home/rrtucci/PycharmProjects/Entanglish
from entanglish.PureStEnt import *
from entanglish.SymNupState import *
Entanglish-Original-Ref
Squashed Entanglement and a Python Implementation Thereof", by R.R.Tucci
In Entanglish-Original-Ref, we derive an analytical formula for the entanglement of any symmetrized n-up state.
Next, we create a symmetrized n-up state and calculate its entanglement for various bi-partitions of the row axes, using both the analytical formula and the definition given above (von Neumann entropy of partial trace). As expected, both definitions give the same answer.
To calculate an entanglement using the von Neumann entropy definition,
it is necessary to calculate the log of a hermitian matrix.
Entanglish allows you to choose among 3 possible methods for
doing this: eigen
, (using an eigenvalue decomposition
of the hermitian matrix), pade
(using Pade approximants) and
pert
(using bootstrapped perturbation theory as
implented in class DenMatPertTheory).
def extra_str(meth, num_steps):
return ', ' + str(num_steps) + ' steps' \
if meth == 'pert' else ''
num_qbits = 4
num_up = 2
dm1 = DenMat(1 << num_qbits, tuple([2]*num_qbits))
st = SymNupState(num_up, num_qbits)
st_vec = st.get_st_vec()
dm1.set_arr_from_st_vec(st_vec)
print('-------------------dm1')
for method in ['eigen', 'pert']:
num_bstrap_steps = 40
print('-----method=' + method +
extra_str(method, num_bstrap_steps))
ecase = PureStEnt(dm1, method,
num_bstrap_steps, verbose=False)
print('entang_023: algo value, known value\n',
ecase.get_entang({0, 2, 3}),
st.get_known_entang(3))
print('entang_02: algo value, known value\n',
ecase.get_entang({0, 2}),
st.get_known_entang(2))
print('entang_1: algo value, known value\n',
ecase.get_entang({1}),
st.get_known_entang(1))
-------------------dm1 -----method=eigen entang_023: algo value, known value 0.6931471805599452 0.6931471805599453 entang_02: algo value, known value 0.8675632284814612 0.8675632284814612 entang_1: algo value, known value 0.6931471805599452 0.6931471805599453 -----method=pert, 40 steps entang_023: algo value, known value 0.9048564000559061 0.6931471805599453 entang_02: algo value, known value 0.9690466827310458 0.8675632284814612 entang_1: algo value, known value 0.6931471805599453 0.6931471805599453
Next, we create a random state vector and
calculate its entanglement for various bi-partitions of the row axes,
using methods eigen
and pert
.
dm2 = DenMat(24, (3, 2, 2, 2))
np.random.seed(123)
st_vec = ut.random_st_vec(24)
dm2.set_arr_from_st_vec(st_vec)
print('-------------------dm2')
num_bstrap_steps = 40
for method in ['eigen', 'pert']:
print('-----method=', method +
extra_str(method, num_bstrap_steps))
ecase = PureStEnt(dm2, method,
num_bstrap_steps, verbose=False)
print('entang_023:', ecase.get_entang({0, 2, 3}))
print('entang_02:', ecase.get_entang({0, 2}))
print('entang_1:', ecase.get_entang({1}))
-------------------dm2 -----method= eigen entang_023: 0.5364009909037474 entang_02: 1.0539432153468993 entang_1: 0.5364009909037474 -----method= pert, 40 steps entang_023: 0.6507512144573282 entang_02: 1.0537529550284028 entang_1: 0.5364009909037474