def _pseudo_inverse_sparse(L, rhoss, method='splu', **pseudo_args): """ Internal function for computing the pseudo inverse of an Liouvillian using sparse matrix methods. See pseudo_inverse for details. """ N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) P = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csr') I = sp.eye(N*N, N*N, format='csr') Q = I - P if pseudo_args['use_rcm']: perm = reverse_cuthill_mckee(L.data) A = sp_permute(L.data, perm, perm, 'csr') Q = sp_permute(Q, perm, perm, 'csr') else: if not settings.has_mkl: A = L.data.tocsc() A.sort_indices() if method == 'splu': if settings.has_mkl: LIQ = mkl_spsolve(A,Q.toarray()) else: lu = sp.linalg.splu(A, permc_spec=pseudo_args['permc_spec'], diag_pivot_thresh=pseudo_args['diag_pivot_thresh'], options=dict(ILU_MILU=pseudo_args['ILU_MILU'])) LIQ = lu.solve(Q.toarray()) elif method == 'spilu': lu = sp.linalg.spilu(A, permc_spec=pseudo_args['permc_spec'], fill_factor=pseudo_args['fill_factor'], drop_tol=pseudo_args['drop_tol']) LIQ = lu.solve(Q.toarray()) else: raise ValueError("unsupported method '%s'" % method) R = sp.csr_matrix(Q * LIQ) if pseudo_args['use_rcm']: rev_perm = np.argsort(perm) R = sp_permute(R, rev_perm, rev_perm, 'csr') return Qobj(R, dims=L.dims)
def _pseudo_inverse_sparse(L, rhoss, method='splu', use_umfpack=False, use_rcm=False): """ Internal function for computing the pseudo inverse of an Liouvillian using sparse matrix methods. See pseudo_inverse for details. """ N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) P = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csc') I = sp.eye(N*N, N*N, format='csc') Q = I - P if use_rcm: perm = reverse_cuthill_mckee(L.data) A = sp_permute(L.data, perm, perm, 'csc').tocsc() Q = sp_permute(Q, perm, perm, 'csc') permc_spec = 'NATURAL' else: A = L.data.tocsc() A.sort_indices() permc_spec = 'COLAMD' if method == 'spsolve': sp.linalg.use_solver(assumeSortedIndices=True, useUmfpack=use_umfpack) LIQ = sp.linalg.spsolve(A, Q) elif method == 'splu': lu = sp.linalg.splu(A, permc_spec=permc_spec) LIQ = lu.solve(Q.toarray()) elif method == 'spilu': lu = sp.linalg.spilu(A, permc_spec=permc_spec, fill_factor=10, drop_tol=1e-8) LIQ = lu.solve(Q.toarray()) else: raise ValueError("unsupported method '%s'" % method) R = sp.csc_matrix(Q * LIQ) if use_rcm: rev_perm = np.argsort(perm) R = sp_permute(R, rev_perm, rev_perm, 'csc') return Qobj(R, dims=L.dims)
def rhot(self, rho0, t, tau): """ Compute the reduced system density matrix :math:`\\rho(t)` Parameters ---------- rho0 : :class:`qutip.Qobj` initial density matrix or state vector (ket) t : float current time tau : float time-delay Returns ------- : :class:`qutip.Qobj` density matrix at time :math:`t` """ if qt.isket(rho0): rho0 = qt.ket2dm(rho0) E = self.propagator(t, tau) rhovec = qt.operator_to_vector(rho0) return qt.vector_to_operator(E*rhovec)
def testOperatorVector(self): """ Superoperator: Operator - vector - operator conversion. """ N = 3 rho1 = rand_dm(N) rho2 = vector_to_operator(operator_to_vector(rho1)) assert_((rho1 - rho2).norm() < 1e-8)
def testOperatorSpreAppl(self): """ Superoperator: apply operator and superoperator from left (spre) """ N = 3 rho = rand_dm(N) U = rand_unitary(N) rho1 = U * rho rho2_vec = spre(U) * operator_to_vector(rho) rho2 = vector_to_operator(rho2_vec) assert_((rho1 - rho2).norm() < 1e-8)
def testOperatorSpostAppl(self): """ Superoperator: apply operator and superoperator from right (spost) """ N = 3 rho = rand_dm(N) U = rand_unitary(N) rho1 = rho * U rho2_vec = spost(U) * operator_to_vector(rho) rho2 = vector_to_operator(rho2_vec) assert_((rho1 - rho2).norm() < 1e-8)
def testOperatorUnitaryTransform(self): """ Superoperator: Unitary transformation with operators and superoperators """ N = 3 rho = rand_dm(N) U = rand_unitary(N) rho1 = U * rho * U.dag() rho2_vec = spre(U) * spost(U.dag()) * operator_to_vector(rho) rho2 = vector_to_operator(rho2_vec) assert_((rho1 - rho2).norm() < 1e-8)
def testMETDDecayAsArray(self): "mesolve: time-dependence as array with super as init cond" me_error = 1e-5 N = 10 a = destroy(N) H = a.dag() * a psi0 = basis(N, 9) rho0vec = operator_to_vector(psi0 * psi0.dag()) E0 = sprepost(qeye(N), qeye(N)) kappa = 0.2 tlist = np.linspace(0, 10, 1000) c_op_list = [[a, np.sqrt(kappa * np.exp(-tlist))]] out1 = mesolve(H, psi0, tlist, c_op_list, []) out2 = mesolve(H, E0, tlist, c_op_list, []) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def outfieldcorr(self, rho0, blist, tlist, tau, c1=None, c2=None): """ Compute output field expectation value <O_n(tn)...O_2(t2)O_1(t1)> for times t1,t2,... and O_i = I, b_out, b_out^\dagger, b_loop, b_loop^\dagger Parameters ---------- rho0 : :class:`qutip.Qobj` initial density matrix or state vector (ket). blist : array_like List of integers specifying the field operators: 0: I (nothing) 1: b_out 2: b_out^\dagger 3: b_loop 4: b_loop^\dagger tlist : array_like list of corresponding times t1,..,tn at which to evaluate the field operators tau : float time-delay c1 : :class:`qutip.Qobj` system collapse operator that couples to the in-loop field in question (only needs to be specified if self.L1 has more than one element) c2 : :class:`qutip.Qobj` system collapse operator that couples to the output field in question (only needs to be specified if self.L2 has more than one element) Returns ------- : complex expectation value of field correlation function """ E = self.outfieldpropagator(blist, tlist, tau) rhovec = qt.operator_to_vector(rho0) return (qt.vector_to_operator(E*rhovec)).tr()
def testMETDDecayAsPartFuncList(self): "mesolve: time-dep. as partial function list with super as init cond" me_error = 1e-5 N = 10 a = destroy(N) H = num(N) psi0 = basis(N, 9) rho0vec = operator_to_vector(psi0 * psi0.dag()) E0 = sprepost(qeye(N), qeye(N)) tlist = np.linspace(0, 10, 100) c_ops = [[[a, partial(lambda t, args, k: np.sqrt(k * np.exp(-t)), k=kappa)]] for kappa in [0.05, 0.1, 0.2]] for idx, kappa in enumerate([0.05, 0.1, 0.2]): out1 = mesolve(H, psi0, tlist, c_ops[idx], []) out2 = mesolve(H, E0, tlist, c_ops[idx], []) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def testMETDDecayAsStrList(self): "mesolve: time-dependence as string list with super as init cond" me_error = 1e-6 N = 10 # number of basis states to consider a = destroy(N) H = a.dag() * a psi0 = basis(N, 9) # initial state rho0vec = operator_to_vector(psi0 * psi0.dag()) E0 = sprepost(qeye(N), qeye(N)) kappa = 0.2 # coupling to oscillator c_op_list = [[a, "sqrt(k*exp(-t))"]] args = {"k": kappa} tlist = np.linspace(0, 10, 100) out1 = mesolve(H, psi0, tlist, c_op_list, [], args=args) out2 = mesolve(H, E0, tlist, c_op_list, [], args=args) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def rhot(rho0,t,tau,H_S,L1,L2,Id,options=qt.Options()): """ Compute rho(t) """ k= int(t/tau)+1 s = t-(k-1)*tau rhovec = qt.operator_to_vector(rho0) G1,E0 = generator(k,H_S,L1,L2) E = integrate(G1,E0,0.,s,opt=options) if k>1: G2,null = generator(k-1,H_S,L1,L2) G2 = qt.composite(Id,G2) E = integrate(G2,E,s,tau,opt=options) E.dims = E0.dims E = TensorQobj(E) for l in range(k-1): E = E.loop() sol = qt.vector_to_operator(E*rhovec) return sol
def testMETDDecayAsFuncList(self): "mesolve: time-dependence as function list with super as init cond" me_error = 1e-6 N = 10 # number of basis states to consider a = destroy(N) H = a.dag() * a psi0 = basis(N, 9) # initial state rho0vec = operator_to_vector(psi0 * psi0.dag()) E0 = sprepost(qeye(N), qeye(N)) kappa = 0.2 # coupling to oscillator def sqrt_kappa(t, args): return np.sqrt(kappa * np.exp(-t)) c_op_list = [[a, sqrt_kappa]] tlist = np.linspace(0, 10, 100) out1 = mesolve(H, psi0, tlist, c_op_list, []) out2 = mesolve(H, E0, tlist, c_op_list, []) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def testSuperJC(self): "mesolve: super vs. density matrix as initial condition" me_error = 1e-6 use_rwa = True N = 4 # number of cavity fock states wc = 2 * np.pi * 1.0 # cavity frequency wa = 2 * np.pi * 1.0 # atom frequency g = 2 * np.pi * 0.1 # coupling strength kappa = 0.05 # cavity dissipation rate gamma = 0.001 # atom dissipation rate pump = 0.25 # atom pump rate # start with an excited atom and maximum number of photons n = N - 2 psi0 = tensor(basis(N, n), basis(2, 1)) rho0vec = operator_to_vector(psi0 * psi0.dag()) tlist = np.linspace(0, 100, 50) out1, out2 = self.jc_integrate(N, wc, wa, g, kappa, gamma, pump, psi0, use_rwa, tlist) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def testMETDDecayAsFunc(self): "mesolve: time-dependence as function with super as init cond" N = 10 # number of basis states to consider a = destroy(N) H = a.dag() * a rho0 = ket2dm(basis(N, 9)) # initial state rho0vec = operator_to_vector(rho0) E0 = sprepost(qeye(N), qeye(N)) kappa = 0.2 # coupling to oscillator def Liouvillian_func(t, args): c = np.sqrt(kappa * np.exp(-t)) * a data = liouvillian(H, [c]).data return data tlist = np.linspace(0, 10, 100) args = {"kappa": kappa} out1 = mesolve(Liouvillian_func, rho0, tlist, [], [], args=args) out2 = mesolve(Liouvillian_func, E0, tlist, [], [], args=args) fid = self.fidelitycheck(out1, out2, rho0vec) assert_(max(abs(1.0 - fid)) < me_error, True)
def test_operator_vector_td(self): "Superoperator: operator_to_vector, time-dependent" assert_(operator_to_vector(self.t1)(.5) == operator_to_vector(self.t1(.5))) vec = operator_to_vector(self.t1) assert_(vector_to_operator(vec)(.5) == vector_to_operator(vec(.5)))
def current_from_ss(ss, L_R, n_c_RC): return -(qt.vector_to_operator(L_R * qt.operator_to_vector(ss)) * n_c_RC).tr()
def current_from_L(L_dict, n_c_RC): ss = steadystate(L_dict['H_S'], [L_dict['L']]) return -(qt.vector_to_operator(L_dict['L_R'] * qt.operator_to_vector(ss)) * n_c_RC).tr()
def countstat_current_noise(L, c_ops, rhoss=None, J_ops=None, R=False): """ Compute the cross-current noise spectrum for a list of collapse operators `c_ops` corresponding to monitored currents, given the system Liouvillian `L`. The current collapse operators `c_ops` should be part of the dissipative processes in `L`, but the `c_ops` given here does not necessarily need to be all collapse operators contributing to dissipation in the Liouvillian. Optionally, the steadystate density matrix `rhoss` and/or the pseudo inverse `R` of the Liouvillian `L`, and the current operators `J_ops` correpsonding to the current collapse operators `c_ops` can also be specified. If `R` is not given, the cross-current correlations will be computed directly without computing `R` explicitly. If either of `rhoss` and `J_ops` are omitted, they will be computed internally. Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. J_ops : array / list (optional) List of current superoperators. R : :class:`qutip.Qobj` (optional) Qobj representing the pseudo inverse of the system Liouvillian `L`. Returns -------- I, S : tuple of arrays The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`) and the zero-frequency cross-current correlation `S`. """ if rhoss is None: rhoss = steadystate(L, c_ops) if J_ops is None: J_ops = [sprepost(c, c.dag()) for c in c_ops] rhoss_vec = mat2vec(rhoss.full()).ravel() N = len(J_ops) I = np.zeros(N) S = np.zeros((N, N)) if R: if R is True: R = pseudo_inverse(L, rhoss) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j] = I[i] S[i, j] -= expect_rho_vec((Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) else: N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) Pop = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csc') Iop = sp.eye(N*N, N*N, format='csc') Q = Iop - Pop A = L.data.tocsc() rhoss_vec = mat2vec(rhoss.full()).ravel() for j, Jj in enumerate(J_ops): Qj = Q * Jj.data * rhoss_vec X_rho_vec = sp.linalg.splu(A, permc_spec='COLAMD').solve(Qj) for i, Ji in enumerate(J_ops): if i == j: S[i, i] = I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j] -= expect_rho_vec(Ji.data * Q, X_rho_vec, 1) return I, S
class TestHusimiQ: @pytest.mark.parametrize('xs', ["", 1, None], ids=['str', 'int', 'none']) def test_failure_if_non_arraylike_coordinates(self, xs): state = qutip.rand_ket(4) valid = np.linspace(-1, 1, 5) with pytest.raises(TypeError) as e: qutip.qfunc(state, xs, valid) assert "must be array-like" in e.value.args[0] with pytest.raises(TypeError) as e: qutip.qfunc(state, valid, xs) assert "must be array-like" in e.value.args[0] with pytest.raises(TypeError) as e: qutip.QFunc(xs, valid) assert "must be array-like" in e.value.args[0] with pytest.raises(TypeError) as e: qutip.QFunc(valid, xs) assert "must be array-like" in e.value.args[0] @pytest.mark.parametrize('ndim', [2, 3]) def test_failure_if_coordinates_not_1d(self, ndim): state = qutip.rand_ket(4) valid = np.linspace(-1, 1, 5) bad = valid.reshape((-1, ) + (1, ) * (ndim - 1)) with pytest.raises(ValueError) as e: qutip.qfunc(state, bad, valid) assert "must be 1D" in e.value.args[0] with pytest.raises(ValueError) as e: qutip.qfunc(state, valid, bad) assert "must be 1D" in e.value.args[0] with pytest.raises(ValueError) as e: qutip.QFunc(bad, valid) assert "must be 1D" in e.value.args[0] with pytest.raises(ValueError) as e: qutip.QFunc(valid, bad) assert "must be 1D" in e.value.args[0] @pytest.mark.parametrize('dm', [True, False], ids=['dm', 'ket']) def test_failure_if_tensor_hilbert_space(self, dm): if dm: state = qutip.rand_dm(4, dims=[[2, 2], [2, 2]]) else: state = qutip.rand_ket(4, dims=[[2, 2], [1, 1]]) xs = np.linspace(-1, 1, 5) with pytest.raises(ValueError) as e: qutip.qfunc(state, xs, xs) assert "must not have tensor structure" in e.value.args[0] with pytest.raises(ValueError) as e: qutip.QFunc(xs, xs)(state) assert "must not have tensor structure" in e.value.args[0] def test_QFunc_raises_if_insufficient_memory(self): xs = np.linspace(-1, 1, 11) state = qutip.rand_ket(4) qfunc = qutip.QFunc(xs, xs, memory=0) with pytest.raises(MemoryError) as e: qfunc(state) assert e.value.args[0].startswith("Refusing to precompute") def test_qfunc_warns_if_insufficient_memory(self): xs = np.linspace(-1, 1, 11) state = qutip.rand_dm(4) with pytest.warns(UserWarning) as e: qutip.qfunc(state, xs, xs, precompute_memory=0) assert (e[0].message.args[0].startswith( "Falling back to iterative algorithm")) @pytest.mark.parametrize('obj', [ pytest.param(np.eye(2, dtype=np.complex128), id='ndarray'), pytest.param([[1, 0], [0, 1]], id='list'), pytest.param(1, id='int'), ]) def test_failure_if_not_a_Qobj(self, obj): xs = np.linspace(-1, 1, 11) with pytest.raises(TypeError) as e: qutip.qfunc(obj, xs, xs) assert e.value.args[0].startswith("state must be Qobj") qfunc = qutip.QFunc(xs, xs) with pytest.raises(TypeError) as e: qfunc(obj) assert e.value.args[0].startswith("state must be Qobj") # Use indirection so that the tests can still be collected if there's a bug # in the generating QuTiP functions. @pytest.mark.parametrize('state', [ pytest.param(lambda: qutip.rand_super(2), id='super'), pytest.param(lambda: qutip.rand_ket(2).dag(), id='bra'), pytest.param(lambda: 1j * qutip.rand_dm(2), id='non-dm operator'), pytest.param(lambda: qutip.Qobj([[1, 0], [0, 0]], dims=[[2], [2, 1]]), id='nonsquare dm'), pytest.param(lambda: qutip.operator_to_vector(qutip.qeye(2)), id='operator-ket'), pytest.param(lambda: qutip.operator_to_vector(qutip.qeye(2)).dag(), id='operator-bra'), ]) def test_failure_if_not_a_state(self, state): xs = np.linspace(-1, 1, 11) state = state() with pytest.raises(ValueError) as e: qutip.qfunc(state, xs, xs) assert (e.value.args[0].startswith( "state must be a ket or density matrix")) qfunc = qutip.QFunc(xs, xs) with pytest.raises(ValueError) as e: qfunc(state) assert (e.value.args[0].startswith( "state must be a ket or density matrix")) @pytest.mark.parametrize('g', [ pytest.param(np.sqrt(2), id='natural units'), pytest.param(1, id='arb units'), ]) @pytest.mark.parametrize('n_ys', [5, 101]) @pytest.mark.parametrize('n_xs', [5, 101]) @pytest.mark.parametrize('dm', [True, False], ids=['dm', 'ket']) @pytest.mark.parametrize('size', [5, 32]) def test_function_and_class_are_equivalent(self, size, dm, n_xs, n_ys, g): xs = np.linspace(-1, 1, n_xs) ys = np.linspace(0, 2, n_ys) state = qutip.rand_dm(size) if dm else qutip.rand_ket(size) function = qutip.qfunc(state, xs, ys, g) class_ = qutip.QFunc(xs, ys, g)(state) np.testing.assert_allclose(function, class_) @pytest.mark.parametrize('g', [ pytest.param(np.sqrt(2), id='natural units'), pytest.param(1, id='arb units'), ]) @pytest.mark.parametrize('n_ys', [5, 101]) @pytest.mark.parametrize('n_xs', [5, 101]) @pytest.mark.parametrize('size', [5, 32]) def test_iterate_and_precompute_are_equivalent(self, size, n_xs, n_ys, g): xs = np.linspace(-1, 1, n_xs) ys = np.linspace(0, 2, n_ys) state = qutip.rand_dm(size) iterate = qutip.qfunc(state, xs, ys, g, precompute_memory=None) precompute = qutip.qfunc(state, xs, ys, g, precompute_memory=np.inf) np.testing.assert_allclose(iterate, precompute) @pytest.mark.parametrize('initial_size', [5, 8]) @pytest.mark.parametrize('dm', [True, False], ids=['dm', 'ket']) def test_same_class_can_take_many_sizes(self, dm, initial_size): xs = np.linspace(-1, 1, 11) ys = np.linspace(0, 2, 11) shape = np.meshgrid(xs, ys)[0].shape sizes = initial_size + np.array([0, 1, -1, 4]) qfunc = qutip.QFunc(xs, ys) for size in sizes: state = qutip.rand_dm(size) if dm else qutip.rand_ket(size) out = qfunc(state) assert isinstance(out, np.ndarray) assert out.shape == shape @pytest.mark.parametrize('dm_first', [True, False]) def test_same_class_can_mix_ket_and_dm(self, dm_first): dms = [True, False, True, False] if not dm_first: dms = dms[::-1] xs = np.linspace(-1, 1, 11) ys = np.linspace(0, 2, 11) shape = np.meshgrid(xs, ys)[0].shape qfunc = qutip.QFunc(xs, ys) for dm in dms: state = qutip.rand_dm(4) if dm else qutip.rand_ket(4) out = qfunc(state) assert isinstance(out, np.ndarray) assert out.shape == shape @pytest.mark.parametrize('n_ys', [5, 101]) @pytest.mark.parametrize('n_xs', [5, 101]) @pytest.mark.parametrize('mix', [0.1, 0.5]) def test_qfunc_is_linear(self, n_xs, n_ys, mix): xs = np.linspace(-1, 1, n_xs) ys = np.linspace(-1, 1, n_ys) qfunc = qutip.QFunc(xs, ys) left, right = qutip.rand_dm(5), qutip.rand_dm(5) qleft, qright = qfunc(left), qfunc(right) qboth = qfunc(mix * left + (1 - mix) * right) np.testing.assert_allclose(mix * qleft + (1 - mix) * qright, qboth) @pytest.mark.parametrize('n_ys', [5, 101]) @pytest.mark.parametrize('n_xs', [5, 101]) @pytest.mark.parametrize('size', [5, 32]) def test_ket_and_dm_give_same_result(self, n_xs, n_ys, size): xs = np.linspace(-1, 1, n_xs) ys = np.linspace(-1, 1, n_ys) state = qutip.rand_ket(size) qfunc = qutip.QFunc(xs, ys) np.testing.assert_allclose(qfunc(state), qfunc(state.proj())) @pytest.mark.parametrize('g', [ pytest.param(np.sqrt(2), id='natural units'), pytest.param(1, id='arb units'), ]) @pytest.mark.parametrize('ys', [ pytest.param(np.linspace(-1, 1, 5), id='(-1,1,5)'), pytest.param(np.linspace(0, 2, 3), id='(0,2,3)'), ]) @pytest.mark.parametrize('xs', [ pytest.param(np.linspace(-1, 1, 5), id='(-1,1,5)'), pytest.param(np.linspace(0, 2, 3), id='(0,2,3)'), ]) @pytest.mark.parametrize('size', [3, 5]) def test_against_naive_implementation(self, xs, ys, g, size): state = qutip.rand_dm(size) state_np = state.full() x, y = np.meshgrid(xs, ys) alphas = 0.5 * g * (x + 1j * y) naive = np.empty(alphas.shape, dtype=np.float64) for i, alpha in enumerate(alphas.flat): coh = qutip.coherent(size, alpha, method='analytic').full() naive.flat[i] = (coh.conj().T @ state_np @ coh).real naive *= (0.5 * g)**2 / np.pi np.testing.assert_allclose(naive, qutip.qfunc(state, xs, ys, g)) np.testing.assert_allclose(naive, qutip.QFunc(xs, ys, g)(state))
def countstat_current_noise(L, c_ops, wlist=None, rhoss=None, J_ops=None, sparse=True, method='direct'): """ Compute the cross-current noise spectrum for a list of collapse operators `c_ops` corresponding to monitored currents, given the system Liouvillian `L`. The current collapse operators `c_ops` should be part of the dissipative processes in `L`, but the `c_ops` given here does not necessarily need to be all collapse operators contributing to dissipation in the Liouvillian. Optionally, the steadystate density matrix `rhoss` and the current operators `J_ops` correpsonding to the current collapse operators `c_ops` can also be specified. If either of `rhoss` and `J_ops` are omitted, they will be computed internally. 'wlist' is an optional list of frequencies at which to evaluate the noise spectrum. Note: The default method is a direct solution using dense matrices, as sparse matrix methods fail for some examples of small systems. For larger systems it is reccomended to use the sparse solver with the direct method, as it avoids explicit calculation of the pseudo-inverse, as described in page 67 of "Electrons in nanostructures" C. Flindt, PhD Thesis, available online: http://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/file_4732600/content Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. wlist : array / list (optional) List of frequencies at which to evaluate (if none are given, evaluates at zero frequency) J_ops : array / list (optional) List of current superoperators. sparse : bool Flag that indicates whether to use sparse or dense matrix methods when computing the pseudo inverse. Default is false, as sparse solvers can fail for small systems. For larger systems the sparse solvers are reccomended. Returns -------- I, S : tuple of arrays The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`) and the zero-frequency cross-current correlation `S`. """ if rhoss is None: rhoss = steadystate(L, c_ops) if J_ops is None: J_ops = [sprepost(c, c.dag()) for c in c_ops] N = len(J_ops) I = np.zeros(N) if wlist is None: S = np.zeros((N, N,1)) wlist=[0.] else: S = np.zeros((N, N,len(wlist))) if sparse == False: rhoss_vec = mat2vec(rhoss.full()).ravel() for k,w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w= w, sparse = sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j,k] = I[i] S[i, j,k] -= expect_rho_vec((Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) else: if method == "direct": N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) Pop = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csr') Iop = sp.eye(N*N, N*N, format='csr') Q = Iop - Pop for k,w in enumerate(wlist): if w != 0.0: L_temp = 1.0j*w*spre(tr_op) + L else: #At zero frequency some solvers fail for small systems. #Adding a small finite frequency of order 1e-15 #helps prevent the solvers from throwing an exception. L_temp = 1.0j*(1e-15)*spre(tr_op) + L if not settings.has_mkl: A = L_temp.data.tocsc() else: A = L_temp.data.tocsr() A.sort_indices() rhoss_vec = mat2vec(rhoss.full()).ravel() for j, Jj in enumerate(J_ops): Qj = Q.dot( Jj.data.dot( rhoss_vec)) try: if settings.has_mkl: X_rho_vec_j = mkl_spsolve(A,Qj) else: X_rho_vec_j = sp.linalg.splu(A, permc_spec ='COLAMD').solve(Qj) except: X_rho_vec_j = sp.linalg.lsqr(A,Qj)[0] for i, Ji in enumerate(J_ops): Qi = Q.dot( Ji.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_i = mkl_spsolve(A,Qi) else: X_rho_vec_i = sp.linalg.splu(A, permc_spec ='COLAMD').solve(Qi) except: X_rho_vec_i = sp.linalg.lsqr(A,Qi)[0] if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[j, i, k] = I[i] S[j, i, k] -= (expect_rho_vec(Jj.data * Q, X_rho_vec_i, 1) + expect_rho_vec(Ji.data * Q, X_rho_vec_j, 1)) else: rhoss_vec = mat2vec(rhoss.full()).ravel() for k,w in enumerate(wlist): R = pseudo_inverse(L,rhoss=rhoss, w= w, sparse = sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec((Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) return I, S
def countstat_current_noise(L, c_ops, wlist=None, rhoss=None, J_ops=None, sparse=True, method='direct'): """ Compute the cross-current noise spectrum for a list of collapse operators `c_ops` corresponding to monitored currents, given the system Liouvillian `L`. The current collapse operators `c_ops` should be part of the dissipative processes in `L`, but the `c_ops` given here does not necessarily need to be all collapse operators contributing to dissipation in the Liouvillian. Optionally, the steadystate density matrix `rhoss` and the current operators `J_ops` correpsonding to the current collapse operators `c_ops` can also be specified. If either of `rhoss` and `J_ops` are omitted, they will be computed internally. 'wlist' is an optional list of frequencies at which to evaluate the noise spectrum. Note: The default method is a direct solution using dense matrices, as sparse matrix methods fail for some examples of small systems. For larger systems it is reccomended to use the sparse solver with the direct method, as it avoids explicit calculation of the pseudo-inverse, as described in page 67 of "Electrons in nanostructures" C. Flindt, PhD Thesis, available online: https://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/file_4732600/content Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. wlist : array / list (optional) List of frequencies at which to evaluate (if none are given, evaluates at zero frequency) J_ops : array / list (optional) List of current superoperators. sparse : bool Flag that indicates whether to use sparse or dense matrix methods when computing the pseudo inverse. Default is false, as sparse solvers can fail for small systems. For larger systems the sparse solvers are reccomended. Returns -------- I, S : tuple of arrays The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`) and the zero-frequency cross-current correlation `S`. """ if rhoss is None: rhoss = steadystate(L, c_ops) if J_ops is None: J_ops = [sprepost(c, c.dag()) for c in c_ops] N = len(J_ops) I = np.zeros(N) if wlist is None: S = np.zeros((N, N, 1)) wlist = [0.] else: S = np.zeros((N, N, len(wlist))) if sparse == False: rhoss_vec = mat2vec(rhoss.full()).ravel() for k, w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w=w, sparse=sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec( (Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) else: if method == "direct": N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) Pop = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csr') Iop = sp.eye(N * N, N * N, format='csr') Q = Iop - Pop for k, w in enumerate(wlist): if w != 0.0: L_temp = 1.0j * w * spre(tr_op) + L else: #At zero frequency some solvers fail for small systems. #Adding a small finite frequency of order 1e-15 #helps prevent the solvers from throwing an exception. L_temp = 1.0j * (1e-15) * spre(tr_op) + L if not settings.has_mkl: A = L_temp.data.tocsc() else: A = L_temp.data.tocsr() A.sort_indices() rhoss_vec = mat2vec(rhoss.full()).ravel() for j, Jj in enumerate(J_ops): Qj = Q.dot(Jj.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_j = mkl_spsolve(A, Qj) else: X_rho_vec_j = sp.linalg.splu( A, permc_spec='COLAMD').solve(Qj) except: X_rho_vec_j = sp.linalg.lsqr(A, Qj)[0] for i, Ji in enumerate(J_ops): Qi = Q.dot(Ji.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_i = mkl_spsolve(A, Qi) else: X_rho_vec_i = sp.linalg.splu( A, permc_spec='COLAMD').solve(Qi) except: X_rho_vec_i = sp.linalg.lsqr(A, Qi)[0] if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[j, i, k] = I[i] S[j, i, k] -= (expect_rho_vec(Jj.data * Q, X_rho_vec_i, 1) + expect_rho_vec(Ji.data * Q, X_rho_vec_j, 1)) else: rhoss_vec = mat2vec(rhoss.full()).ravel() for k, w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w=w, sparse=sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec( (Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) return I, S
return noisy_cliffords[rand_int] # Some SPAM noise_operator_state_prep = unitary_channel(4, 0.8, 0.01) noise_operator_meas_op = unitary_channel(4, 0.8, 0.01) noise_operator_state_prep.dims = [[[2, 2], [2, 2]], [[2, 2], [2, 2]]] noise_operator_meas_op.dims = [[[2, 2], [2, 2]], [[2, 2], [2, 2]]] # Set the number of runs number_of_seq = 5000 seq_length = 25 # Set observable Q Q = qt.tensor(qt.sigmay() - qt.sigmaz(), qt.sigmay() - qt.sigmaz()) Q.dims = [[2, 2], [2, 2]] Q = qt.operator_to_vector(Q) Q = noise_operator_meas_op * Q # Set initial state rho = qt.basis(4, 0) * qt.basis(4, 0).dag() rho.dims = [[2, 2], [2, 2]] rho = qt.operator_to_vector(rho) rho = noise_operator_state_prep * rho def fixed_func(m, c0, c1, c2, c3, e1): return (c0 + c1 * e1**(m - 1.) + c2 * (sub_unitarity_A_to_A(two_qubit_noise))**(m - 1.) + c3 * (sub_unitarity_B_to_B(two_qubit_noise))**(m - 1.)) # Run! reference_standard_data = protocol(noisy_cliffords, rho, Q, seq_length,
def to_pauli(self, H): H.dims = self.ops[0].dims HP = [(qt.operator_to_vector(H).dag() * qt.operator_to_vector(p))[0][0][0] for p in self.ops] return qt.Qobj(np.array(HP)) / np.sqrt(len(self.ops))
def ramsey_experiment(left_gate_list, right_gate_list, L, field_f, transition_f, nCounts, time_per_gate, gate_switching_time, \ experiment_sample_time, time_units=1e-6, noise_type=None, freq_list=0,amp_list=0, phase_list=0,\ start_f=0, stop_f=0, fluctuators=0, plot_noise = False): #left and right gate_lists: lists of the gates sandwiching the Gi gates #L: the number of Gi gates. If you want to vary this, make it a list #field_f: frequency of external field in Hz. To vary this, make it a list. Either L or field_f must vary. #transition_f: frequency of the qubit transition in Hz. #time_per_gate: total time to complete one Gx, Gy, Gi, or Gz #switching_time: additional time before you start any new gate or change gates #experiment_sample_time: time at which you trigger a single expeimrent (single count), usually 60 Hz triggered #nCounts: number of times to repeat one experiment (a single set of parameters) #time_units: baseline units (in seconds) for any additional drift noise signal you may want to add. Defaults to ms. #check that the input has something to be varied if type(L) == type(field_f): print("Error: Either L or field_f must vary over a range!") return None #this list will contain the varied parameter, either detuning in Hz or delay time in s varied_param = [] if type(L) == list: total_experiments = len(L) experiment_list = [] for l in L: experiment_list.append(left_gate_list + ['Gi'] * l + right_gate_list) field_f_list = [field_f] * total_experiments varied_param = [(time_per_gate * l + gate_switching_time) for l in L] else: total_experiments = len(field_f) experiment_list = [left_gate_list + ['Gi'] * L + right_gate_list ] * total_experiments field_f_list = field_f varied_param = delta_list #create a noise object: if noise_type != None and noise_type == "Sine": total_time = total_experiments * nCounts * experiment_sample_time + 1 #assumes that every count is taken every 0.016 seconds; no experiment takes longer sig = _ns.NoiseSignalSine(time_unit=time_units) sig.configure_noise(resolution_factor=1, freq_list=freq_list, amp_list=amp_list, phase_list=phase_list, total_time=total_time / time_units) sig.init() if plot_noise == True: sig.plot_noise_signal() probs = [ ] #a list of total_expeirment* elements. Has the probability for each experiment set (assumes constant p for all nCounts) time_per_experiment_set = [ ] #has the time at the start of each experiment set ones_counts = [] #number of 1s counted for each experiment set angles = [] #total theta rotation for each experiment set ideal_angles = [] #has the ideal theta rotation for each experiment set transition_f_list = [ ] #list of the transition frequency at the start of each experiment set detuning_list = [ ] #list of the detuning at the start of each experiment set absolute_time = 0 #seconds rho0 = _qt.operator_to_vector(_qt.ket2dm(_qt.basis(2, 0))) rho1 = _qt.operator_to_vector(_qt.ket2dm(_qt.basis(2, 1))) for experiment_index in range(total_experiments): experiment = experiment_list[experiment_index] compressed_experiment = compress_gate_list(experiment) #the following line assumes that each count for each experiment set is triggered at 0.0167 seconds (no single gate sequence can be longer than 1/60) absolute_time += experiment_sample_time * nCounts #print("Starting experiment set {} at {} s".format(experiment_index, absolute_time)) rho = rho0 total_angle = 0 total_ideal_angle = 0 modified_transition_f = transition_f detuning = field_f_list[experiment_index] - modified_transition_f for gate_name, repetitions in compressed_experiment: if noise_type != None: if absolute_time >= total_time: print("Abs time: {}, total_time: {}".format( absolute_time, total_time)) detuning_noise = sig[absolute_time / time_units] else: detuning_noise = 0 if gate_name == 'Gx': angle = (np.pi / 2) * repetitions ideal_angle = (np.pi / 2) * repetitions rho = (_qt.to_super(_qt.rx(angle))) * rho #this section just keeps the angle between 0 and pi angle = angle % (2 * np.pi) ideal_angle = ideal_angle % (2 * np.pi) if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) elif gate_name == 'Gy': angle = (np.pi / 2) * repetitions ideal_angle = (np.pi / 2) * repetitions rho = (_qt.to_super(_qt.ry(angle))) * rho #this section just keeps the angle between 0 and pi angle = angle % (2 * np.pi) ideal_angle = ideal_angle % (2 * np.pi) if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) elif gate_name == "Gi": #print("starting {} with z-rotation 2pi*{:.2f}".format(gate_list_to_string(experiment), detuning*(time_per_gate*repetitions + gate_switching_time))) #make the transition frequency oscillate between a fraction of its nominal value modified_transition_f = transition_f * (1 + detuning_noise) detuning = field_f_list[ experiment_index] - modified_transition_f angle = 2 * np.pi * detuning * (time_per_gate * repetitions + gate_switching_time) rho = (_qt.to_super(_qt.rz(angle))) * rho total_angle += angle total_ideal_angle += ideal_angle #calculate probabilities of being in 1 after the all the gates in the experiment have been applied p1 = (rho.dag() * rho1).norm() #fix the p1 if it exceeds 1 due to rounding error if p1 > 1: p1 = 2 - p1 #get nCounts of data (bits) for the experiment one_counts = np.random.binomial(nCounts, p1) angles.append(total_angle) ideal_angles.append(total_ideal_angle) probs.append(p1) ones_counts.append(one_counts) time_per_experiment_set.append(absolute_time) transition_f_list.append(modified_transition_f) detuning_list.append(detuning) return np.asarray(ones_counts), np.asarray( time_per_experiment_set), np.asarray(probs), np.asarray(varied_param)
def create_data(time_per_count, num_samples, num_counts, gate_list, time_unit, noise_type=None, walking_amp=None, telegraph_amp=None, \ res=None, freq_list=None, amp_list=None, phase_list=None, start_f=None, stop_f=None, fluctuators=None, plot_noise=False, \ add_noise=False, noise_object=None, dc_angle_offset=0, constant_linear_drift=0): #time_per_shot: time in seconds for a single (prep-gate-measure+delay) #num_samples: how many timestamps and strings of counts you want to have #num_counts: how many data points to create (how many ones and zeros) per sample (i.e. per timestamp) --> affects both time of a sample and precision #num_shots: how many times (shots) you apply (prep-gate_list-meas) to get one count (a single 0 or 1) --> determines the time of one count, but won't affect precision #gate_list: gates you want to do for your operation, entered as a list of strings #xerr,yerr,zerr: 2D tuples with overrotation amplitude in radians and frequency in Hz #constant linear drift: enter in rads/second rho0 = _qt.operator_to_vector(_qt.ket2dm(_qt.basis(2, 0))) rho1 = _qt.operator_to_vector(_qt.ket2dm(_qt.basis(2, 1))) zero_counts = [] one_counts = [] timestep = num_counts * time_per_count #the time to get a full bitstring of zeros and ones for one sample, i.e. one timestamp timestamps = np.arange( timestep, num_samples * timestep, timestep) #array of 1*timestep, 2*timestep,....(num_samples)*timestep probs = [] total_time = (time_per_count * num_counts * num_samples) / time_unit sig = 0 if noise_type == "Sine": #this function returns the noise object so you can enter it back in as a parameter # in the event that you call the function repeatedly for an identical set of parameters if noise_object != None: sig = noise_object #print("REUSING NOISE OBJECT") while total_time > sig.times[-1] + timestep: sig.next_interval() #print("Doing a NEXT INTERVAL") else: #print("INITIALIZING NEW NOISE") sig = _ns.NoiseSignalSine(time_unit=time_unit) sig.configure_noise(resolution_factor=res, freq_list=freq_list, amp_list=amp_list, phase_list=phase_list, total_time=total_time) sig.init() if add_noise != None: sig.add_random_noise( add_noise ) #add normal noise with specified std deviation if requested elif noise_type == "Random Walk": sig = _ns.NoiseSignalRandomWalk(initial_seed=1234, time_unit=time_unit) sig.configure_noise(walking_amp, res, total_time) sig.init() elif noise_type == "Telegraph": sig = _ns.NoiseSignalTelegraph(initial_seed=1234, time_unit=time_unit) sig.configure_noise(exponent=1, amplitude=telegraph_amp, total_fluctuators=fluctuators, start_freq=start_f, stop_freq=stop_f, total_time=total_time) sig.init() sig.interpolation_settings(do_interpolation=True, resolution_factor=res) if plot_noise == True: sig.plot_noise_signal() angle_list = [] expected_angle_list = [] compressed_gate_list = compress_gate_list(gate_list) for time in timestamps: noise_at_time = 0 if noise_type != None: #print(time/time_unit) noise_at_time = sig[time / time_unit] rho = rho0 total_angle = 0 total_ideal_angle = 0 for gate in compressed_gate_list: gate_name = gate[0] gate_repetitions = gate[1] ''' Next step: calculate change in rotation error within each shot. Currently takes the time at the start of the experiment shot and applies that to all gates in one shot. Depending on the timescale of the error and time per shot, this simplification may need to be addressed so that each gate, say each Gx in (Gx)^11, has an error associated with its specific time, not the same error for all 11 Gx gates. ''' if gate_name == 'Gx': angle = ( np.pi / 2 + noise_at_time ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time ideal_angle = ( np.pi / 2 ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time rho = (_qt.to_super(_qt.rx(angle))) * rho #this section just keeps the angle between 0 and pi angle = angle % (2 * np.pi) ideal_angle = ideal_angle % (2 * np.pi) if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) elif gate_name == 'Gy': angle = ( np.pi / 2 + noise_at_time ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time ideal_angle = ( np.pi / 2 ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time rho = (_qt.to_super(_qt.ry(angle))) * rho #this section just keeps the angle between 0 and pi angle = angle % (2 * np.pi) ideal_angle = ideal_angle % (2 * np.pi) if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) elif gate_name == 'Gz': angle = ( np.pi / 2 + noise_at_time ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time ideal_angle = ( np.pi / 2 ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time rho = (_qt.to_super(_qt.rz(angle))) * rho #this section just keeps the angle between 0 and pi angle = angle % (2 * np.pi) ideal_angle = ideal_angle % (2 * np.pi) if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) elif gate_name == "Gi": #apply only the oscillating drift angle, don't add it to pi/2 angle = ( noise_at_time ) * gate_repetitions + dc_angle_offset + constant_linear_drift * time ideal_angle = 0 + dc_angle_offset + constant_linear_drift * time print("applying Gi with angle ", angle) rho = (_qt.to_super(_qt.rz(angle))) * rho angle = angle % (2 * np.pi) ideal_angle = 0 if angle > np.pi: angle = 2 * np.pi - angle if angle < 0: angle = 0 + abs(angle) if ideal_angle > np.pi: ideal_angle = 2 * np.pi - ideal_angle if ideal_angle < 0: ideal_angle = 0 + abs(ideal_angle) total_angle += angle total_ideal_angle += ideal_angle #append the total rotation angle after timestamp 'time' angle_list.append(total_angle) expected_angle_list.append(total_ideal_angle) #calculate probabilities of being in 1 after the experiment has been applied p1 = (rho.dag() * rho1).norm() #fix the p1 if it exceeds 1 due to rounding error if p1 > 1: #print("p1 exceeds 1 for time {}".format(time)) #print("prob is {}".format(p1)) #print("Resetting to {}".format(2-p1)) p1 = 2 - p1 probs.append(p1) one_count = np.random.binomial( num_counts, p1 ) #simulates a summation of the number of 1-counts you get in one bitstring sample zero_count = num_counts - one_count #simulates summation of the number of 0-counts in one bitstring sample one_counts.append(one_count) zero_counts.append(zero_count) if plot_noise == True: plt.plot(timestamps, np.asarray(expected_angle_list), label="Ideal Angle", ls='dashed', color='orange') plt.plot(timestamps, np.asarray(angle_list), label="Drifting Angle") plt.legend() plt.xlabel("Time, seconds") plt.yticks(np.linspace(0, np.pi, 5), ['0', '$\pi/4$', '$\pi/2$', '$3\pi/4$', '$\pi$']) plt.ylabel("Angle, radians\n(Displayed between 0 and $\pi$)") plt.title("Time-Dependent Rotation Angle after each {}".format( gate_list_to_string(gate_list))) plt.grid() plt.show() plt.figure(figsize=(15, 3)) plt.plot(timestamps, probs) plt.ylim(0, 1) plt.xlim(timestamps[0], timestamps[-1]) plt.xlabel("Time, seconds") plt.ylabel("Probability of Measuring State {1}") plt.title("Simulated {} with {} Noise".format( gate_list_to_string(gate_list), noise_type)) plt.grid() plt.show() return (np.asarray(one_counts), np.asarray(zero_counts), np.asarray(timestamps), probs, np.asarray(expected_angle_list), np.asarray(angle_list), sig)
def get_average_gate_fidelity(self, runs=500, target=None): """ returns average gate fidelity Args runs (int) : number of runs to compute the average gate fidelity target (4x4 numpy array) : target unitary to compute fidelity """ self.solver_obj.calculate_evolution(self.init, self.total_time, 10000, 1) U_ideal = self.solver_obj.get_unitary()[0] self.solver_obj.add_noise_static(2 * np.pi * self.H_zeeman, 18 * 1e-6) self.solver_obj.add_noise_generic(2 * np.pi * self.H_heisenberg, oneoverfnoise, self.exchange_dc / 100) self.solver_obj.calculate_evolution(self.init, self.total_time, 10000, int(runs)) U_list = self.solver_obj.get_unitary() # Calculate the averaged super operator in the Lioville superoperator form using column convention basis = [qt.basis(4, it) for it in range(4)] superoperator_basis = [ basis_it1 * basis_it2.dag() for basis_it2 in basis for basis_it1 in basis ] averaged_map = np.zeros([16, 16], dtype=np.complex) for u in U_list: temp_U = qt.Qobj(u) output_density = list() for it in range(len(superoperator_basis)): temp_vec = np.array( qt.operator_to_vector( temp_U * superoperator_basis[it] * temp_U.dag() / float(runs)).full()).flatten() output_density.append(np.array(temp_vec)) averaged_map = np.add(averaged_map, np.array(output_density)) # Define the target unitary operation if target is None: target = sp.linalg.expm( -np.pi / 2. * 1j * sp.sparse.diags([0., -1., -1., 0.]).todense()) # get phase from optimizing noiseless unitary evolution def to_minimize_fidelity(theta): temp_z_gate = np.matmul( sp.linalg.expm(-2 * np.pi * 1j * theta * self.H_zeeman), U_ideal) temp_m = np.matmul(sp.conjugate(sp.transpose(target)), temp_z_gate) return np.real(1. - (sp.trace( np.matmul(temp_m, sp.conjugate(sp.transpose(temp_m)))) + np.abs(sp.trace(temp_m))**2.) / 20.) ideal_phase = sp.optimize.minimize( to_minimize_fidelity, [self.delta_z * (self.total_time * 1e-9)], method='Nelder-Mead', tol=1e-6).x[0] target = np.matmul( sp.linalg.expm(2 * np.pi * 1j * ideal_phase * self.H_zeeman), target) # Change the shape of the averaged super operator to match the definitions used in QuTip (row convention) target = qt.Qobj(target) averaged_map = qt.Qobj(averaged_map).trans() averaged_map._type = 'super' averaged_map.dims = [[[4], [4]], [[4], [4]]] averaged_map.superrep = qt.to_super(target).superrep # Calculate the average gate fidelity of the superoperator with the target unitary gate fidelity = qt.average_gate_fidelity(averaged_map, target) return fidelity
def ttmsolve(dynmaps, rho0, times, e_ops=[], learningtimes=None, tensors=None, **kwargs): """ Solve time-evolution using the Transfer Tensor Method, based on a set of precomputed dynamical maps. Parameters ---------- dynmaps : list of :class:`qutip.Qobj` List of precomputed dynamical maps (superoperators), or a callback function that returns the superoperator at a given time. rho0 : :class:`qutip.Qobj` Initial density matrix or state vector (ket). times : array_like list of times :math:`t_n` at which to compute :math:`\\rho(t_n)`. Must be uniformily spaced. e_ops : list of :class:`qutip.Qobj` / callback function single operator or list of operators for which to evaluate expectation values. learningtimes : array_like list of times :math:`t_k` for which we have knowledge of the dynamical maps :math:`E(t_k)`. tensors : array_like optional list of precomputed tensors :math:`T_k` kwargs : dictionary Optional keyword arguments. See :class:`qutip.nonmarkov.ttm.TTMSolverOptions`. Returns ------- output: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`. """ opt = TTMSolverOptions(dynmaps=dynmaps, times=times, learningtimes=learningtimes, **kwargs) diff = None if isket(rho0): rho0 = ket2dm(rho0) output = Result() e_sops_data = [] if callable(e_ops): n_expt_op = 0 expt_callback = True else: try: tmp = e_ops[:] del tmp n_expt_op = len(e_ops) expt_callback = False if n_expt_op == 0: # fall back on storing states opt.store_states = True for op in e_ops: e_sops_data.append(spre(op).data) if op.isherm and rho0.isherm: output.expect.append(np.zeros(len(times))) else: output.expect.append(np.zeros(len(times), dtype=complex)) except TypeError: raise TypeError("Argument 'e_ops' should be a callable or" + "list-like.") if tensors is None: tensors, diff = _generatetensors(dynmaps, learningtimes, opt=opt) rho0vec = operator_to_vector(rho0) K = len(tensors) states = [rho0vec] for n in range(1, len(times)): states.append(None) for k in range(n): if n-k < K: states[-1] += tensors[n-k]*states[k] for i, r in enumerate(states): if opt.store_states or expt_callback: if r.type == 'operator-ket': states[i] = vector_to_operator(r) else: states[i] = r if expt_callback: # use callback method e_ops(times[i], states[i]) for m in range(n_expt_op): if output.expect[m].dtype == complex: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 0) else: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 1) output.solver = "ttmsolve" output.times = times output.ttmconvergence = diff if opt.store_states: output.states = states return output
def test_operator_vector_td(self): "Superoperator: operator_to_vector, time-dependent" assert_( operator_to_vector(self.t1)(.5) == operator_to_vector(self.t1(.5))) vec = operator_to_vector(self.t1) assert_(vector_to_operator(vec)(.5) == vector_to_operator(vec(.5)))
def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args): """ Internal function for computing the pseudo inverse of an Liouvillian using sparse matrix methods. See pseudo_inverse for details. """ N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) P = zcsr_kron(rhoss_vec.data, tr_op_vec.data.T) I = sp.eye(N * N, N * N, format='csr') Q = I - P if w is None: L = 1.0j * (1e-15) * spre(tr_op) + L else: if w != 0.0: L = 1.0j * w * spre(tr_op) + L else: L = 1.0j * (1e-15) * spre(tr_op) + L if pseudo_args['use_rcm']: perm = reverse_cuthill_mckee(L.data) A = sp_permute(L.data, perm, perm) Q = sp_permute(Q, perm, perm) else: if ss_args['solver'] == 'scipy': A = L.data.tocsc() A.sort_indices() if pseudo_args['method'] == 'splu': if settings.has_mkl: A = L.data.tocsr() A.sort_indices() LIQ = mkl_spsolve(A, Q.toarray()) else: pspec = pseudo_args['permc_spec'] diag_p_thresh = pseudo_args['diag_pivot_thresh'] pseudo_args = pseudo_args['ILU_MILU'] lu = sp.linalg.splu(A, permc_spec=pspec, diag_pivot_thresh=diag_p_thresh, options=dict(ILU_MILU=pseudo_args)) LIQ = lu.solve(Q.toarray()) elif pseudo_args['method'] == 'spilu': lu = sp.linalg.spilu(A, permc_spec=pseudo_args['permc_spec'], fill_factor=pseudo_args['fill_factor'], drop_tol=pseudo_args['drop_tol']) LIQ = lu.solve(Q.toarray()) else: raise ValueError("unsupported method '%s'" % method) R = sp.csr_matrix(Q * LIQ) if pseudo_args['use_rcm']: rev_perm = np.argsort(perm) R = sp_permute(R, rev_perm, rev_perm, 'csr') return Qobj(R, dims=L.dims)
def to_pauli(H): global pauli_op_names, pauli_ops, pbasis HP = [(qt.operator_to_vector(H).dag()*qt.operator_to_vector(p))[0][0][0] for p in pauli_ops] V = qt.Qobj(np.array(HP))/np.sqrt(len(pauli_ops)) H2 = sum([h*pauli_ops[i] for i, h in enumerate(V.full().T[0])]) return V
] e_lst_sm = [e_lst[0]] ham_off = rest_ham + jc_ham pulse_fun = lambda t, amp: jc.tanh_updown(t, amp, sigma, 5. * sigma, tau / 2. + 5. * sigma) pulse_dot_fun = lambda t, amp: jc.tanh_updown_dot(t, amp, sigma, 5. * sigma, tau / 2. + 5. * sigma) times = np.linspace(0., tau, steps) if __name__ == '__main__': e_vecs = [qt.operator_to_vector(e_op).data.H for e_op in e_lst] e_cb = lambda rho: jc.expectation_cb(e_vecs, rho) sim_dict = { 'ham_off': ham_off, 'pulse_ham': pulse_ham, 'pulse_fun': lambda t: pulse_fun(t, 90.), 'pulse_dot_fun': lambda t: pulse_dot_fun(t, 90.), 'c_ops': c_lst, 'cb_func': jc.five_checks, 'meas_op': np.sqrt(kappa) * a, 'rho_init': qt.operator_to_vector(rho0).data.todense(), 'times': times, 'n_traj': 1 }