def entangling_power(U): """ Calculate the entangling power of a two-qubit gate U, which is zero of nonentangling gates and 1 and 2/9 for maximally entangling gates. Parameters ---------- U : qobj Qobj instance representing a two-qubit gate. Returns ------- ep : float The entanglement power of U (real number between 0 and 1) References: Explorations in Quantum Computing, Colin P. Williams (Springer, 2011) """ if not U.isoper: raise Exception("U must be an operator.") if U.dims != [[2, 2], [2, 2]]: raise Exception("U must be a two-qubit gate.") from qutip.qip.operations.gates import swap a = (tensor(U, U).dag() * swap(N=4, targets=[1, 3]) * tensor(U, U) * swap(N=4, targets=[1, 3])) b = (tensor(swap() * U, swap() * U).dag() * swap(N=4, targets=[1, 3]) * tensor(swap() * U, swap() * U) * swap(N=4, targets=[1, 3])) return 5.0/9 - 1.0/36 * (a.tr() + b.tr()).real
def qft_steps(N=1, swapping=True): """ Quantum Fourier Transform operator on N qubits returning the individual steps as unitary matrices operating from left to right. Parameters ---------- N: int Number of qubits. swap: boolean Flag indicating sequence of swap gates to be applied at the end or not. Returns ------- U_step_list: list of qobj List of Hadamard and controlled rotation gates implementing QFT. """ if N < 1: raise ValueError("Minimum value of N can be 1") U_step_list = [] if N == 1: U_step_list.append(snot()) else: for i in range(N): for j in range(i): U_step_list.append( cphase(np.pi / (2**(i - j)), N, control=i, target=j)) U_step_list.append(snot(N, i)) if swapping: for i in range(N // 2): U_step_list.append(swap(N, [N - i - 1, i])) return U_step_list
def swap_map(x): W = swap() S = (1j * x * W).expm() S._type = None S.dims = [[[2], [2]], [[2], [2]]] S.superrep = 'super' return S
def test_swap(self): states = [qutip.rand_ket(2) for _ in [None]*2] start = qutip.tensor(states) swapped = qutip.tensor(states[::-1]) swap = gates.swap() assert _infidelity(swapped, swap*start) < 1e-12 assert _infidelity(start, swap*swap*start) < 1e-12
def testExpandGate2toNSwap(self): """ gates: expand 2 to N (using swap) """ a, b = np.random.rand(), np.random.rand() k1 = (a * basis(2, 0) + b * basis(2, 1)).unit() c, d = np.random.rand(), np.random.rand() k2 = (c * basis(2, 0) + d * basis(2, 1)).unit() N = 6 kets = [rand_ket(2) for k in range(N)] for m in range(N): for n in set(range(N)) - {m}: psi_in = tensor([ k1 if k == m else k2 if k == n else kets[k] for k in range(N) ]) psi_out = tensor([ k2 if k == m else k1 if k == n else kets[k] for k in range(N) ]) targets = [m, n] G = swap(N, targets) psi_out = G * psi_in assert_((psi_out - G * psi_in).norm() < 1e-12)
def testSwapGate(self): """ gates: swap gate """ a, b = np.random.rand(), np.random.rand() psi1 = (a * basis(2, 0) + b * basis(2, 1)).unit() c, d = np.random.rand(), np.random.rand() psi2 = (c * basis(2, 0) + d * basis(2, 1)).unit() psi_in = tensor(psi1, psi2) psi_out = tensor(psi2, psi1) psi_res = swap() * psi_in assert_((psi_out - psi_res).norm() < 1e-12) psi_res = swap() * swap() * psi_in assert_((psi_in - psi_res).norm() < 1e-12)
def test_EntanglingPower(): "Entropy: Entangling power" assert_(abs(entangling_power(cnot()) - 2 / 9) < 1e-12) assert_(abs(entangling_power(iswap()) - 2 / 9) < 1e-12) assert_(abs(entangling_power(berkeley()) - 2 / 9) < 1e-12) assert_(abs(entangling_power(sqrtswap()) - 1 / 6) < 1e-12) alpha = 2 * np.pi * np.random.rand() assert_( abs( entangling_power(swapalpha(alpha)) - 1 / 6 * np.sin(np.pi * alpha)**2) < 1e-12) assert_(abs(entangling_power(swap()) - 0) < 1e-12)
def test_known_iscptp(self): """ Superoperator: ishp, iscp, istp and iscptp known cases. """ def case(qobj, shouldhp, shouldcp, shouldtp): hp = qobj.ishp cp = qobj.iscp tp = qobj.istp cptp = qobj.iscptp shouldcptp = shouldcp and shouldtp if (hp == shouldhp and cp == shouldcp and tp == shouldtp and cptp == shouldcptp): return fails = [] if hp != shouldhp: fails.append(("ishp", shouldhp, hp)) if tp != shouldtp: fails.append(("istp", shouldtp, tp)) if cp != shouldcp: fails.append(("iscp", shouldcp, cp)) if cptp != shouldcptp: fails.append(("iscptp", shouldcptp, cptp)) raise AssertionError("Expected {}.".format(" and ".join([ "{} == {} (got {})".format(fail, expected, got) for fail, expected, got in fails ]))) # Conjugation by a creation operator should # have be CP (and hence HP), but not TP. a = create(2).dag() S = sprepost(a, a.dag()) case(S, True, True, False) # A single off-diagonal element should not be CP, # nor even HP. S = sprepost(a, a) case(S, False, False, False) # Check that unitaries are CPTP and HP. case(identity(2), True, True, True) case(sigmax(), True, True, True) # Check that unitaries on bipartite systems are CPTP and HP. case(tensor(sigmax(), identity(2)), True, True, True) # Check that a linear combination of bipartitie unitaries is CPTP and HP. S = (to_super(tensor(sigmax(), identity(2))) + to_super(tensor(identity(2), sigmay()))) / 2 case(S, True, True, True) # The partial transpose map, whose Choi matrix is SWAP, is TP # and HP but not CP (one negative eigenvalue). W = Qobj(swap(), type='super', superrep='choi') case(W, True, False, True) # Subnormalized maps (representing erasure channels, for instance) # can be CP but not TP. subnorm_map = Qobj(identity(4) * 0.9, type='super', superrep='super') case(subnorm_map, True, True, False) # Check that things which aren't even operators aren't identified as # CPTP. case(basis(2), False, False, False)
msg = (qt.basis(2, 0) + qt.basis(2, 1)).unit() state = qt.tensor(msg, TFD) XYZ_msg = {"I": qt.tensor(qt.identity(2), IDrest),\ "X": qt.tensor(qt.sigmax(), IDrest),\ "Y": qt.tensor(qt.sigmay(), IDrest),\ "Z": qt.tensor(qt.sigmaz(), IDrest)} XYZ_L_ = dict([(op_name, qt.tensor(qt.identity(2), op)) for op_name, op in XYZ_L.items()]) XYZ_R_ = dict([(op_name, qt.tensor(qt.identity(2), op)) for op_name, op in XYZ_R.items()]) P = pauli_basis(2) SWAP = swap(N=2, targets=[0, 1]) SWAPc = op_coeffs(SWAP, P) INSERT = sum([ coeff * XYZ_msg[op_name[0]] * XYZ_L_[op_name[1]] for op_name, coeff in SWAPc.items() ]) HL_ = qt.tensor(qt.identity(2), HL) HR_ = qt.tensor(qt.identity(2), HR) SIZE = qt.tensor(qt.identity(2), sum([cf_.dag() * cf_ for cf_ in cf[2:]])) def wormhole(state, g, t=10): global HL, HR, SIZE return (-1j * HR_ * t).expm() * (1j * g * SIZE).expm() * ( -1j * HL_ * t).expm() * INSERT * (1j * HL_ * t).expm() * state
class TestSuperopReps: """ A test class for the QuTiP function for applying superoperators to subsystems. """ def test_SuperChoiSuper(self, superoperator): """ Superoperator: Converting superoperator to Choi matrix and back. """ choi_matrix = to_choi(superoperator) test_supe = to_super(choi_matrix) # Assert both that the result is close to expected, and has the right # type. assert (test_supe - superoperator).norm() < tol assert choi_matrix.type == "super" and choi_matrix.superrep == "choi" assert test_supe.type == "super" and test_supe.superrep == "super" @pytest.mark.parametrize('dimension', [2, 4]) def test_SuperChoiChiSuper(self, dimension): """ Superoperator: Converting two-qubit superoperator through Choi and chi representations goes back to right superoperator. """ superoperator = super_tensor( rand_super(dimension), rand_super(dimension), ) choi_matrix = to_choi(superoperator) chi_matrix = to_chi(choi_matrix) test_supe = to_super(chi_matrix) # Assert both that the result is close to expected, and has the right # type. assert (test_supe - superoperator).norm() < tol assert choi_matrix.type == "super" and choi_matrix.superrep == "choi" assert chi_matrix.type == "super" and chi_matrix.superrep == "chi" assert test_supe.type == "super" and test_supe.superrep == "super" def test_ChoiKrausChoi(self, superoperator): """ Superoperator: Convert superoperator to Choi matrix and back. """ choi_matrix = to_choi(superoperator) kraus_ops = to_kraus(choi_matrix) test_choi = kraus_to_choi(kraus_ops) # Assert both that the result is close to expected, and has the right # type. assert (test_choi - choi_matrix).norm() < tol assert choi_matrix.type == "super" and choi_matrix.superrep == "choi" assert test_choi.type == "super" and test_choi.superrep == "choi" def test_NonSquareKrausSuperChoi(self): """ Superoperator: Convert non-square Kraus operator to Super + Choi matrix and back. """ zero = asarray([[1], [0]], dtype=complex) one = asarray([[0], [1]], dtype=complex) zero_log = kron(kron(zero, zero), zero) one_log = kron(kron(one, one), one) # non-square Kraus operator (isometry) kraus = Qobj(zero_log @ zero.T + one_log @ one.T) super = sprepost(kraus, kraus.dag()) choi = to_choi(super) op1 = to_kraus(super) op2 = to_kraus(choi) op3 = to_super(choi) assert choi.type == "super" and choi.superrep == "choi" assert super.type == "super" and super.superrep == "super" assert (op1[0] - kraus).norm() < tol assert (op2[0] - kraus).norm() < tol assert (op3 - super).norm() < tol def test_NeglectSmallKraus(self): """ Superoperator: Convert Kraus to Choi matrix and back. Neglect tiny Kraus operators. """ zero = asarray([[1], [0]], dtype=complex) one = asarray([[0], [1]], dtype=complex) zero_log = kron(kron(zero, zero), zero) one_log = kron(kron(one, one), one) # non-square Kraus operator (isometry) kraus = Qobj(zero_log @ zero.T + one_log @ one.T) super = sprepost(kraus, kraus.dag()) # 1 non-zero Kraus operator the rest are zero sixteen_kraus_ops = to_kraus(super, tol=0.0) # default is tol=1e-9 one_kraus_op = to_kraus(super) assert len(sixteen_kraus_ops) == 16 and len(one_kraus_op) == 1 assert (one_kraus_op[0] - kraus).norm() < tol def test_SuperPreservesSelf(self, superoperator): """ Superoperator: to_super(q) returns q if q is already a supermatrix. """ assert superoperator is to_super(superoperator) def test_ChoiPreservesSelf(self, superoperator): """ Superoperator: to_choi(q) returns q if q is already Choi. """ choi = to_choi(superoperator) assert choi is to_choi(choi) def test_random_iscptp(self, superoperator): """ Superoperator: Randomly generated superoperators are correctly reported as CPTP and HP. """ assert superoperator.iscptp assert superoperator.ishp # Conjugation by a creation operator a = create(2).dag() S = sprepost(a, a.dag()) # A single off-diagonal element S_ = sprepost(a, a) # Check that a linear combination of bipartite unitaries is CPTP and HP. S_U = (to_super(tensor(sigmax(), identity(2))) + to_super(tensor(identity(2), sigmay()))) / 2 # The partial transpose map, whose Choi matrix is SWAP ptr_swap = Qobj(swap(), type='super', superrep='choi') # Subnormalized maps (representing erasure channels, for instance) subnorm_map = Qobj(identity(4) * 0.9, type='super', superrep='super') @pytest.mark.parametrize(['qobj', 'shouldhp', 'shouldcp', 'shouldtp'], [ pytest.param(S, True, True, False, id="conjugatio by create op"), pytest.param(S_, False, False, False, id="single off-diag"), pytest.param(identity(2), True, True, True, id="Identity"), pytest.param(sigmax(), True, True, True, id="Pauli X"), pytest.param( tensor(sigmax(), identity(2)), True, True, True, id="bipartite system", ), pytest.param( S_U, True, True, True, id="linear combination of bip. unitaries", ), pytest.param(ptr_swap, True, False, True, id="partial transpose map"), pytest.param(subnorm_map, True, True, False, id="subnorm map"), pytest.param(basis(2), False, False, False, id="not an operator"), ]) def test_known_iscptp(self, qobj, shouldhp, shouldcp, shouldtp): """ Superoperator: ishp, iscp, istp and iscptp known cases. """ assert qobj.ishp == shouldhp assert qobj.iscp == shouldcp assert qobj.istp == shouldtp assert qobj.iscptp == (shouldcp and shouldtp) def test_choi_tr(self, dimension): """ Superoperator: Trace returned by to_choi matches docstring. """ assert abs(to_choi(identity(dimension)).tr() - dimension) <= tol def test_stinespring_cp(self, dimension): """ Stinespring: A and B match for CP maps. """ superop = rand_super_bcsz(dimension) A, B = to_stinespring(superop) assert norm(A - B) < tol @pytest.mark.repeat(3) def test_stinespring_agrees(self, dimension): """ Stinespring: Partial Tr over pair agrees w/ supermatrix. """ map = rand_super_bcsz(dimension) state = rand_dm_ginibre(dimension) S = to_super(map) A, B = to_stinespring(map) q1 = vector_to_operator(S * operator_to_vector(state)) # FIXME: problem if Kraus index is implicitly # ptraced! q2 = (A * state * B.dag()).ptrace((0, )) assert (q1 - q2).norm('tr') <= tol def test_stinespring_dims(self, dimension): """ Stinespring: Check that dims of channels are preserved. """ chan = super_tensor(to_super(sigmax()), to_super(qeye(dimension))) A, B = to_stinespring(chan) assert A.dims == [[2, dimension, 1], [2, dimension]] assert B.dims == [[2, dimension, 1], [2, dimension]] @pytest.mark.parametrize('dimension', [2, 4, 8]) def test_chi_choi_roundtrip(self, dimension): superop = rand_super_bcsz(dimension) superop = to_chi(superop) rt_superop = to_chi(to_choi(superop)) dif = norm(rt_superop - superop) assert dif == pytest.approx(0, abs=1e-7) assert rt_superop.type == superop.type assert rt_superop.dims == superop.dims chi_sigmax = [[0, 0, 0, 0], [0, 4, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] chi_diag2 = [[4, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] rotX_pi_4 = (-1j * sigmax() * pi / 4).expm() chi_rotX_pi_4 = [[2, 2j, 0, 0], [-2j, 2, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] @pytest.mark.parametrize(['superop', 'chi_expected'], [ pytest.param(sigmax(), chi_sigmax), pytest.param(to_super(sigmax()), chi_sigmax), pytest.param(qeye(2), chi_diag2), pytest.param(rotX_pi_4, chi_rotX_pi_4) ]) def test_chi_known(self, superop, chi_expected): """ Superoperator: Chi-matrix for known cases is correct. """ chi_actual = to_chi(superop) chiq = Qobj( chi_expected, dims=[[[2], [2]], [[2], [2]]], superrep='chi', ) assert (chi_actual - chiq).norm() < tol