def test_partial_transpose_bipartite(): """partial transpose of bipartite systems""" rho = Qobj(np.arange(16).reshape(4, 4), dims=[[2, 2], [2, 2]]) # no transpose rho_pt = partial_transpose(rho, [0, 0]) assert_(np.abs(np.max(rho_pt.full() - rho.full())) < 1e-12) # partial transpose subsystem 1 rho_pt = partial_transpose(rho, [1, 0]) rho_pt_expected = np.array([[0, 1, 8, 9], [4, 5, 12, 13], [2, 3, 10, 11], [6, 7, 14, 15]]) assert_(np.abs(np.max(rho_pt.full() - rho_pt_expected)) < 1e-12) # partial transpose subsystem 2 rho_pt = partial_transpose(rho, [0, 1]) rho_pt_expected = np.array([[0, 4, 2, 6], [1, 5, 3, 7], [8, 12, 10, 14], [9, 13, 11, 15]]) assert_(np.abs(np.max(rho_pt.full() - rho_pt_expected)) < 1e-12) # full transpose rho_pt = partial_transpose(rho, [1, 1]) assert_(np.abs(np.max(rho_pt.full() - rho.trans().full())) < 1e-12)
def test_partial_transpose_comparison(): """partial transpose: comparing sparse and dense implementations""" N = 10 rho = tensor(rand_dm(N, density=0.5), rand_dm(N, density=0.5)) # partial transpose of system 1 rho_pt1 = partial_transpose(rho, [1, 0], method="dense") rho_pt2 = partial_transpose(rho, [1, 0], method="sparse") np.abs(np.max(rho_pt1.full() - rho_pt1.full())) < 1e-12 # partial transpose of system 2 rho_pt1 = partial_transpose(rho, [0, 1], method="dense") rho_pt2 = partial_transpose(rho, [0, 1], method="sparse") np.abs(np.max(rho_pt1.full() - rho_pt2.full())) < 1e-12
def test_partial_transpose_randomized(): """partial transpose: randomized tests on tripartite system""" rho = tensor(rand_dm(2, density=1), rand_dm(2, density=1), rand_dm(2, density=1)) mask = np.random.randint(2, size=3) rho_pt_ref = _partial_transpose_reference(rho, mask) rho_pt1 = partial_transpose(rho, mask, method="dense") np.abs(np.max(rho_pt1.full() - rho_pt_ref.full())) < 1e-12 rho_pt2 = partial_transpose(rho, mask, method="sparse") np.abs(np.max(rho_pt2.full() - rho_pt_ref.full())) < 1e-12
def peres_horodecki_bipartite(rho, mask=[0,1]): """ Tests the given bipartite state for Peres-Horodecki criterion Parameters ---------- rho: qobj/array-like Density operator for the state mask: list of int, length 2 mask used for partial transpose Returns ------- isentangled: bool True for entangled, False for disentangled """ if rho.type != 'oper': raise TypeError("Input must be a density matrix") rhopt = partial_transpose(rho,mask) rhopt_eigs = rhopt.eigenenergies() if min(rhopt_eigs) < 0: isentangled = True else: isentangled = False return isentangled
def log_neg(rho,mask=[1,0]): """ Calculate the logarithmic negativity for a density matrix Parameters: ----------- rho : qobj/array-like Input density matrix Returns: -------- logneg: Logarithmic Negativity """ if rho.type != 'oper': raise TypeError("Input must be a density matrix") rhopt = partial_transpose(rho,mask) logneg = log2( rhopt.norm() ) return logneg
def negativity(rho,mask=[1,0]): """ Calculate the negativity for a density matrix Parameters: ----------- rho : qobj/array-like Input density matrix Returns: -------- neg : Negativity """ if rho.type != 'oper': raise TypeError("Input must be a density matrix") rhopt = partial_transpose(rho,mask) neg = ( rhopt.norm() - 1 ) / 2 return neg
def peres_horodecki_bipartite(rho, mask=[0, 1]): """ Tests the given bipartite state for Peres-Horodecki criterion Parameters ---------- rho: qobj/array-like Density operator for the state mask: list of int, length 2 mask used for partial transpose Returns ------- isentangled: bool True for entangled, False for disentangled """ if rho.type != 'oper': raise TypeError("Input must be a density matrix") rhopt = partial_transpose(rho, mask) rhopt_eigs = rhopt.eigenenergies() if min(rhopt_eigs) < 0: isentangled = True else: isentangled = False return isentangled
def least_ent_eig(r, theta, a_z): rho=(1/4)*(qt.tensor(qt.qeye(2), qt.qeye(2)) + qt.tensor(a_z*qt.sigmaz(), qt.qeye(2))\ - r*np.sin(theta)/np.sqrt(2)*qt.tensor(qt.sigmax(),qt.sigmax())\ - r*np.sin(theta)/np.sqrt(2)*qt.tensor(qt.sigmay(),qt.sigmay())\ - r*np.cos(theta)*qt.tensor(qt.sigmaz(),qt.sigmaz())) rho_pt = qt.partial_transpose(rho, [0, 1]) eigs = np.linalg.eigvalsh(rho_pt.full()) return min(eigs)
def negativity(rho, mask=[1, 0]): """ Calculate the negativity for a density matrix Parameters: ----------- rho : qobj/array-like Input density matrix Returns: -------- neg : Negativity """ if rho.type != 'oper': raise TypeError("Input must be a density matrix") rhopt = partial_transpose(rho, mask) neg = (rhopt.norm() - 1) / 2 return neg
def create_dataset(n_samples): """Create dataset. Parameters: n_samples(int): Number of samples Output: _states(list): States associated with each set of measurements. _measurements(list): Measurements. _labels(list): Label associated with each set of measurements. """ _states = [] _labels = [] _measurements = [] #Basis Measured name_basis = ['I', 'X', 'Y', 'Z'] basis = [qutip.identity(2), qutip.sigmax(),qutip.sigmay(),qutip.sigmaz()] for _ in range(n_samples): density = qutip.rand_dm(4, density=0.75, dims=[[2,2],[2,2]]) #Partial Transpose density_partial_T = qutip.partial_transpose(density, [0,1]) #Labels: 1 if entangled 0 if separable (PPT Criterion) if (density_partial_T.eigenenergies() < 0).any(): _labels.append(1) else: _labels.append(-1) _states.append(density) val_measurements = measurement(density_matrix=density, base=basis, name_base=name_basis) _measurements.append(val_measurements) return _states, _measurements, _labels
def generate_separable(n_samples): """Create n_samples separable states. Parameters: n_samples(int): Number of samples Output: states_train(list): States associated with each set of measurements. measurements_train(list): measurements """ _states = [] _measurements = [] #Basis Measured name_basis = ['I', 'X', 'Y', 'Z'] basis = [qutip.identity(2), qutip.sigmax(),qutip.sigmay(),qutip.sigmaz()] counter = 0 while not counter == n_samples: density = qutip.rand_dm(4, density=0.75, dims=[[2,2],[2,2]]) #Partial Transpose density_partial_T = qutip.partial_transpose(density, [0,1]) #Labels: 1 if entangled 0 if separable (PPT Criterion) if (density_partial_T.eigenenergies() < 0).any(): pass else: _states.append(density) val_measurements = measurement(density_matrix=density, base=basis, name_base=name_basis) _measurements.append(val_measurements) counter += 1 return _states, _measurements
def entangled(state): if state.shape == (4, 4): return (np.any(q.partial_transpose(state, [1, 0]).eigenenergies() < 0)) else: raise TypeError( 'The Peres-Horodecki criterion only works for pairs of qubits.')
def simulate_process_prep_measure(E_AB, prep_meas_settings): """ Simulate measurements for process tomography using the prepare-and-measure scheme, given a "true" quantum process `E_AB`, a list of input states and measurement settings specified by `prep_meas_settings`. The process `E_AB` should be a :py:class:`qutip.Qobj` containing the Choi matrix of the channel being applied, as a :py:class:`qutip.Qobj` matrix (NOT as a superoperator). It should not be normalized, i.e., we expect :math:`\mathrm{tr}_B(E_{AB}) = \mathbb{I}_A`. The argument `prep_meas_settings` must be a list of tuples `(sigma_in, Mk_out, num_repeats)`, where `sigma_in` is an input state specified as a :py:class:`qutip.Qobj` object, where `Mk_out` is a POVM specified as a list of POVM effects (each POVM effect is a positive semidefinite matrix of norm ≤ 1, and is specified as a :py:class:`qutip.Qobj` matrix), and finally `num_repeats` is the number of times to repeat this setting. Returns: an object `d` with properties `d.Emn_ch`, `d.Nm`, representing the arguments suitable for :py:meth:`QPtomographer.channelspace.run()` and the simulated frequency counts. """ Emn_ch = [] Nm = [] dimA = E_AB.dims[0][0] dimB = E_AB.dims[0][1] dimAB = dimA * dimB for (sigma_in, Mk_out, num_repeats) in prep_meas_settings: # calculate the output state after sending sigma_in through the process rho_out = (E_AB * qutip.tensor(qutip.partial_transpose(sigma_in, [1]), qutip.qeye(dimB))).ptrace(1) # output POVM num_out_effects = len(Mk_out) # get some random numbers for this measurement setting x = np.random.rand(num_repeats) proboffset = 0 # We sample the measurement outcomes as follows: we split the interval # [0,1] in number sections equal to the number of possible outcomes, # each of length = probability of that outcome. Then, for each possible # outcome, we count the number of random numbers in `x` that fall into # the corresponding section. for Mk in Mk_out: p = qutip.expect(Mk, rho_out) # = trace(Mk * rho_out) Emn_ch.append( qutip.tensor(qutip.partial_transpose(sigma_in, [1]), Mk).data.toarray().astype(dtype=complex)) Nm.append( np.count_nonzero((proboffset <= x) & (x < proboffset + p))) proboffset += p # sanity check assert np.abs(proboffset - 1.0) < 1e-6 d = _Store() d.Emn_ch = Emn_ch d.Nm = np.array(Nm) return d
# Set initial state sys = cv.System(N, 2) r = np.arcsinh(np.sqrt(mu)) #sys.apply_SMS(r) sys.apply_TMS(r) # Make state no Gaussian k = 0.4 sys.apply_photon_subtraction(k, 1) psi0 = sys.state if psi0.isket: rho0 = psi0 * psi0.dag() # State with partial transpose rho = qt.partial_transpose(rho0, [1, 0]) # Method 1 - Fastest norm = rho.norm() log_neg = np.log2(norm) # Method 2 rho2 = rho * rho.dag() norm2 = (rho2.sqrtm()).tr() log_neg2 = np.log2(norm2) # Method 3 - for Gaussian states - best approx for small N? sys.set_state(rho) sys.set_quadratures_basis() cm = sys.get_full_CM() print(cm)