The purpose of this notebook is to construct a maximally entangled state vector and its corresponding density matrix, and then to calculate the "entanglement profile" of that state.
In Entaglish, density matrices are stored in the class DenMat. That class contains attributes: num_rows, row_shape and arr. arr is a numpy array of shape=(num_rows, num_rows). row_shape is a tuple such that the product of its components is num_rows. For example, a state with row_shape=(2,3,4) consists of 3 qudits with d=2,3,4 and num_rows=24.
See Entanglish-Original-Ref for an explicit definition of the maximally entangled states that we use. The basic requirement for a density matrix $\rho_{\rvx, \rvy}$ to be maximally entangled is for its partial trace $\rho_{\rvx}={\rm tr}_\rvy \rho_{\rvx, \rvy}$ to be a diagonal matrix with all terms in the diagonal equal to the same constant. The sum of the diagonal elements must of course be one. For example, $\rho_{\rvx}={\rm diag}(0.25, 0.25, 0.25, 0.25)$. (If $\rvx$ and $\rvy$ have different numbers of possible values, this assumes that $\rvx$ is the one with the smaller number of values.)
Given a state with num_row_axes qudits, one can define a (bipartite) entanglement for each possible bi-partitions of range( num_row_axes). By a bi-partition we mean two nonempty disjoint subsets whose union is range(num_row_axes). An entanglement profile is a dictionary mapping bi-partition half-size to a dictionary that maps each bi-partition of that half-size to its entanglement.
Entanglish-Original-Ref
Squashed Entanglement and a Python Implementation Thereof", by R.R.Tucci
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.DenMat import *
from entanglish.MaxEntangState import *
from entanglish.EntangCase import *
from entanglish.PureStEnt import *
Create a DenMat object called dm_max with num_rows=24 and row_shape=(2, 2, 3 ,2).Then set its arr to a maximally entangled state.
dm_max = DenMat(24, (2, 2, 3, 2))
max_ent_st = MaxEntangState(dm_max.num_rows, dm_max.row_shape,
[0, 1, 3], [2])
EntangCase.check_max_entang_st(max_ent_st)
st_vec = max_ent_st.get_st_vec()
entang = max_ent_st.get_known_entang()
dm_max.set_arr_from_st_vec(st_vec)
print('st_vec=\n', st_vec)
print("entang=", entang)
st_vec= [0.57735027+0.j 0. +0.j 0. +0.j 0.57735027+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.57735027+0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j] entang= 1.0986122886681098
Class PureStEnt is a child of class EntangCase.
All objects with EntangCase as a parent calculate entanglement numerically, from an algorithm, not from a known analytical formula. For a pure state, that algo is the von Neumann entropy of a partial trace of dm.
All objects with EntangCase as a parent inherit methods for calculating and printing entanglement profiles
ecase = PureStEnt(dm_max, 'eigen')
pf = ecase.get_entang_profile()
ecase.print_entang_profiles([pf], dm_max.row_shape)
bi-partition half-size=1 (0 | 1, 2, 3) : -0.00000, max-entang= 0.69315 (1 | 0, 2, 3) : 0.63651, max-entang= 0.69315 (2 | 0, 1, 3) : 1.09861, max-entang= 1.09861 (3 | 0, 1, 2) : 0.63651, max-entang= 0.69315 bi-partition half-size=2 (0, 1 | 2, 3) : 0.63651, max-entang= 1.38629 (0, 2 | 1, 3) : 1.09861, max-entang= 1.38629 (0, 3 | 1, 2) : 0.63651, max-entang= 1.38629 (1, 2 | 0, 3) : 0.63651, max-entang= 1.38629 (1, 3 | 0, 2) : 1.09861, max-entang= 1.38629 (2, 3 | 0, 1) : 0.63651, max-entang= 1.38629