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 schm_decompo(to_decomp): """ Schmidt decompo of states/operators - split it in two even partites""" if is_ket(to_decomp): init_type = 'ket' data = to_decomp.full() d = int(np.sqrt(np.size(data))) data = np.reshape(data, [d, d]) elif is_oper(to_decomp): init_type = 'oper' data = custom_reorder_2q(to_decomp.full()) else: data=to_decomp if(np.ndim(data) == 1): init_type = 'ket' elif (np.ndim(data) == 1) and (data.shape[0] == data.shape[1]): init_type = 'oper' else: raise SystemError('data type/shape not understood: {} / {}'.format) U, D, Vdag = np.linalg.svd(data) V = np.transpose(Vdag) res = [] for i, d in enumerate(D): if(d>1e-6): if(init_type == 'ket'): u = Qobj(np.array(U[:, i])[:, np.newaxis]) v = Qobj(np.array(V[:, i])[:, np.newaxis]) res.append((d, u, v)) elif(init_type == 'oper'): u = Qobj(devect(U[:, i])) v = Qobj(devect(V[:, i])) res.append((d, u, v)) return res
def _compute_prop_grad(self, k, j, compute_prop=True): """ Calculate the gradient of propagator wrt the control amplitude in the timeslot using the exponentiation of the the augmented matrix. The propagtor is calculated for 'free' in this method and hence it is returned if compute_prop==True Returns: [prop], prop_grad """ dyn = self.parent dg = dyn._get_phased_dyn_gen(k) aug = self._get_aug_mat(k, j) if dyn.oper_dtype == Qobj: aug_exp = aug.expm() prop_grad = Qobj(aug_exp.data[:dg.shape[0], dg.shape[1]:], dims=dyn.dyn_dims) if compute_prop: prop = Qobj(aug_exp.data[:dg.shape[0], :dg.shape[1]], dims=dyn.dyn_dims) else: aug_exp = la.expm(aug) prop_grad = aug_exp[:dg.shape[0], dg.shape[1]:] if compute_prop: prop = aug_exp[:dg.shape[0], :dg.shape[1]] if compute_prop: return prop, prop_grad else: return prop_grad
def selector(num): up_x = Qobj([1, 1]).unit().dag() down_x = Qobj([1, -1]).unit().dag() if num == 0: return up_x else: return down_x
def test_score(params, x, y): """Calculates the average fidelity between the predicted and output kets for a given on the whole dataset. Args: params (obj:`np.ndarray`): parameters :math:`\t` and :math:`\tau` in :math: `U^{\dagger} U(\vec{t},\vec{\tau})` inputs (obj:`np.ndarray`): input kets :math:`|\psi_{l} \rangle` in the dataset outputs (obj:`np.ndarray`): output kets :math:`U(\vec{t}, \vec{\tau}) |ket_{input}\rangle` in the dataset Returns: float: fidelity between :math:`U(\vec{t}, \vec{\tau})|ket_{input} \rangle` and the output (label) kets for parameters :math:`\vec{t}, \vec{\tau}` averaged over the entire training set. """ fidel = 0 for i in range(train_len): pred = np.matmul(make_unitary(N, params), x[i]) step_fidel = fidelity(Qobj(pred), Qobj(y[i])) fidel += step_fidel return fidel / train_len
def test_superradiant(self): """ PIQS: Test the calculation of the superradiant state density matrix. The state is |N/2, 0> for N even and |N/2, 0.5> for N odd. The matrix has size (O(N^2), O(N^2)) in Dicke basis ('dicke'). The matrix has size (2^N, 2^N) in the uncoupled basis ('uncoupled'). """ N = 3 true_state = np.zeros((6, 6)) true_state[1, 1] = 1 true_state = Qobj(true_state) test_state = superradiant(N) assert_equal(test_state, true_state) N = 4 true_state = np.zeros((9, 9)) true_state[2, 2] = 1 true_state = Qobj(true_state) test_state = superradiant(N) assert_equal(test_state, true_state) # uncoupled test_state_uncoupled = superradiant(2, basis="uncoupled") assert_array_equal(test_state_uncoupled.dims, [[2, 2], [2, 2]]) assert_array_equal(test_state_uncoupled.shape, (4, 4)) assert_almost_equal(test_state_uncoupled.full()[1, 1], 1+0j)
def schm_decompo(to_decomp): """ Schmidt decompo of states/operators - split it in two even partites""" if (type(to_decomp) == Qobj): if (to_decomp.type == 'ket'): init_type = 'ket' data = to_decomp.full() dsquare = np.size(data) d = int(np.sqrt(dsquare)) data = np.reshape(data, [d, d]) elif (to_decomp.type == 'oper'): init_type = 'oper' data = to_decomp.full() data = custom_reorder(data) else: data = to_decomp U, D, Vdag = np.linalg.svd(data) V = np.transpose(Vdag) res = [] for i, d in enumerate(D): if (d > 1e-6): if (init_type == 'ket'): u = Qobj(np.array(U[:, i])[:, np.newaxis]) v = Qobj(np.array(V[:, i])[:, np.newaxis]) res.append((d, u, v)) elif (init_type == 'oper'): u = Qobj(devect(U[:, i])) v = Qobj(devect(V[:, i])) res.append((d, u, v)) return res
def propagator_steadystate(U): """Find the steady state for successive applications of the propagator :math:`U`. Parameters ---------- U : qobj Operator representing the propagator. Returns ------- a : qobj Instance representing the steady-state vector. """ evals,evecs = la.eig(U.full()) ev_min, ev_idx = get_min_and_index(abs(evals-1.0)) N = int(sqrt(len(evals))) evecs = evecs.T rho = Qobj(vec2mat(evecs[ev_idx])) return rho * (1 / real(rho.tr()))
def expand_state(state: Qobj, dims): num_focks = dims[0][1] old_num_focks = state.dims[0][1] new_full = zeros((2 * num_focks, 1), dtype=complex) new_full[0:old_num_focks, 0] = state.full()[0:old_num_focks, 0] new_full[num_focks:num_focks + old_num_focks, 0] = state.full()[old_num_focks: , 0] return Qobj(new_full, dims=dims)
def d_sum(a: Qobj, b: Qobj) -> Qobj: """Perform the direct sum, or kroenecker sum of two matrices. Specifically here we use quantum objects. A direct sum simply involves taking two matrices and putting them in either corner of a larger zero matrix. d_sum(a,b) : [ a 0 ] [ 0 b ] Args: a (Qobj): First matrix b (Qobj): Second matrix Returns: Qobj: Resulting matrix """ tot_size = [a.shape[0] + b.shape[0], a.shape[1] + b.shape[1]] matrix = np.zeros(tot_size, dtype=complex) if isinstance(a, Qobj): a = a.full() if isinstance(b, Qobj): b = b.full() for i, row in enumerate(a): for j, val in enumerate(row): matrix[i][j] = val for i, row in enumerate(b): for j, val in enumerate(row): matrix[i + a.shape[0]][j + a.shape[1]] = val return Qobj(matrix)
def propagator_steadystate(U): """Find the steady state for successive applications of the propagator :math:`U`. Parameters ---------- U : qobj Operator representing the propagator. Returns ------- a : qobj Instance representing the steady-state vector. """ evals, evecs = la.eig(U.full()) ev_min, ev_idx = get_min_and_index(abs(evals - 1.0)) N = int(sqrt(len(evals))) evecs = evecs.T rho = Qobj(vec2mat(evecs[ev_idx])) return rho * (1 / real(rho.tr()))
def solve_method_2(input_): t = 1e-6 def Omega(_t: float, *args) -> float: if _t <= 0 or _t >= t: return 0 # scaling = 1 / 100 scaling = input_ return np.sin(_t / t * np.pi) * scaling t_list = np.linspace(0, t, 500) Omegas = np.array([Omega(_t) for _t in t_list]) area = simpson(Omegas, t_list) print(f"Area: {area}") time_independent_terms = Qobj( np.zeros((3, 3)) + Vdd * 1e9 * rcrt @ rcrt.T) Omega_coeff_terms = Qobj((rcrt @ rc1t.T + rc1t @ rcrt.T) / 2) solver = mesolve( [time_independent_terms, [Omega_coeff_terms, Omega]], psi_0, t_list, options=Options(store_states=True, nsteps=20000), ) c_r1 = np.abs(solver.states[-1].data[1, 0]) return c_r1
def test_povm(): """ Test if povm formulation works correctly by checking probabilities for the quantum state discrimination example """ coeff = (sqrt(2) / (1 + sqrt(2))) E_1 = coeff * ket2dm(basis(2, 1)) E_2 = coeff * ket2dm((basis(2, 0) - basis(2, 1)) / (sqrt(2))) E_3 = identity(2) - E_1 - E_2 M_1 = Qobj(scipy.linalg.sqrtm(E_1)) M_2 = Qobj(scipy.linalg.sqrtm(E_2)) M_3 = Qobj(scipy.linalg.sqrtm(E_3)) ket1 = basis(2, 0) ket2 = (basis(2, 0) + basis(2, 1)) / (sqrt(2)) dm1 = ket2dm(ket1) dm2 = ket2dm(ket2) M = [M_1, M_2, M_3] _, probabilities = measurement_statistics_povm(ket1, M) np.testing.assert_allclose(probabilities, [0, 0.293, 0.707], 0.001) _, probabilities = measurement_statistics_povm(ket2, M) np.testing.assert_allclose(probabilities, [0.293, 0, 0.707], 0.001) _, probabilities = measurement_statistics_povm(dm1, M) np.testing.assert_allclose(probabilities, [0, 0.293, 0.707], 0.001) _, probabilities = measurement_statistics_povm(dm2, M) np.testing.assert_allclose(probabilities, [0.293, 0, 0.707], 0.001)
def rand_ket(N,density=1,dims=None): """Creates a random Nx1 sparse ket vector. Parameters ---------- N : int Number of rows for output quantum operator. density : float Density between [0,1] of output ket state. Returns ------- oper : qobj Nx1 ket state quantum operator. Other Parameters ---------------- dims : list Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[1]]. """ if dims: _check_dims(dims,N,1) X = sp.rand(N,1,density,format='csr') X.data=X.data-0.5 Y=X.copy() Y.data=1.0j*np.random.random(len(X.data))-(0.5+0.5j) X=X+Y X=Qobj(X) if dims: return Qobj(X/X.norm(),dims=dims,shape=[N,1]) else: return Qobj(X/X.norm())
def __init__(self, interaction: LaserInteraction, init_state: Qobj, target_state: Qobj, num_steps: int, energy_weight: float = 0, max_energy: float = 0, step_duration: float = 1): assert isinstance(init_state, Qobj) assert init_state.isket assert isinstance(target_state, Qobj) assert target_state.isket assert isinstance(interaction, LaserInteraction) self._interaction = interaction self._init_state = init_state.full() self._target_state = target_state.full() self._num_steps = num_steps self._energy_weight = energy_weight self._max_energy = max_energy self._step_duration = step_duration self._c_ops = [op.full() for op in interaction.control_operators] self._optim_records = [] self._cache_param_vec = None self._cache_evolved_state = None
def test_k(self): """ lattice: Test the method Lattice1d.k(). """ L = 7 lattice_L123 = Lattice1d(num_cell=L, boundary="periodic", cell_num_site=1, cell_site_dof=[2, 3]) kq = lattice_L123.k() kop = np.zeros((L, L), dtype=complex) for row in range(L): for col in range(L): if row == col: kop[row, col] = (L - 1) / 2 else: kop[row, col] = 1 / (np.exp(2j * np.pi * (row - col) / L) - 1) kt = np.kron(kop * 2 * np.pi / L, np.eye(6)) dim_H = [[2, 2, 3], [2, 2, 3]] kt = Qobj(kt, dims=dim_H) [k_q, Vq] = kq.eigenstates() [k_t, Vt] = kt.eigenstates() k_tC = k_t - 2 * np.pi / L * ((L - 1) // 2) # k_ts = [(i-(L-1)//2)*2*np.pi/L for i in range(L)] # k_w = np.kron((np.array(k_ts)).T, np.ones((1,6))) assert_((np.abs(k_tC - k_q) < 1E-13).all())
def test_cell_periodic_parts(self): """ lattice: Test the method Lattice1d.array_of_unk(). """ # Coupled Resonator Optical Waveguide(CROW) Example(PhysRevB.99.224201) J = 2 eta = np.pi / 4 H_cell = Qobj(np.array([[0, J * np.sin(eta)], [J * np.sin(eta), 0]])) inter_cell_T0 = (J / 2) * Qobj( np.array([[np.exp(eta * 1j), 0], [0, np.exp(-eta * 1j)]])) inter_cell_T1 = (J / 2) * Qobj(np.array([[0, 1], [1, 0]])) inter_cell_T = [inter_cell_T0, inter_cell_T1] CROW_lattice = Lattice1d(num_cell=4, boundary="periodic", cell_num_site=1, cell_site_dof=[2], Hamiltonian_of_cell=H_cell, inter_hop=inter_cell_T) (kxA, val_kns) = CROW_lattice.get_dispersion() (knxA, vec_kns) = CROW_lattice.cell_periodic_parts() (knxA, qH_ks) = CROW_lattice.bulk_Hamiltonians() for i in range(4): for j in range(2): if val_kns[j][i] == 0: E_V = Qobj(vec_kns[i, j, :]) eE_V = qH_ks[i] * E_V assert_(np.max(abs(eE_V)) < 1.0E-12) else: E_V = Qobj(vec_kns[i, j, :]) eE_V = qH_ks[i] * E_V qE_V = np.divide(eE_V, E_V) oE = val_kns[j][i] * np.ones((2, 1)) assert_(np.max(abs(oE - qE_V)) < 1.0E-12)
def split_AB(m, block_info, n, as_qobj=False): """Split a sparse matrix into two block-diagonal matrices Args: m (scipy.sparse.spmatrix or qutip.Qobj): The sparse matrix to operate on block_info (np.ndarray): Array of block size information n (int): dimension of `m` Returns: tuple[scipy.sparse.csr_matrix]: a tuple of two sparse matrices, where each matrix is block-diagonal Assumptions on `m`: - contains data only in the upper triangle - consists of rectangular blocks sitting on the diagonal The size of the rectangular blocks is specified in the `block_info` array (0 values in that array are discarded). The values in `block_info` is the number of rows in each consecutive block. The matrix is split in such a way that alternating blocks are separated into alternating result matrices. This guarantees that the two resulting matrices are block diagonal (with only the upper-left quadrant of each block non-zero) """ if isinstance(m, Qobj): m = m.data assert (scipy.sparse.triu(m) - m).nnz == 0 m_coo = m.tocoo() assert np.all(np.sort(m_coo.row) == m_coo.row) assert block_info.dtype == np.int32 data, row, col, nnz = m_coo.data, m_coo.row, m_coo.col, m_coo.nnz block_info = iter([n for n in block_info if n != 0]) selector = 1 n_row_limit = next(block_info) - 1 # how far can we look ahead? AB = { 0: {'data': [], 'row': [], 'col': []}, 1: {'data': [], 'row': [], 'col': []}, } for k in range(nnz): i = row[k] j = col[k] v = data[k] if i > n_row_limit: n_row_limit += next(block_info) selector = (selector + 1) % 2 # toggle 0↔1 AB[selector]['data'].append(v) AB[selector]['row'].append(i) AB[selector]['col'].append(j) res = tuple( scipy.sparse.coo_matrix( (np.array(d['data']), (np.array(d['row']), np.array(d['col']))), shape=(n, n), ).tocsr() for d in [AB[0], AB[1]] ) if as_qobj: res = Qobj(res[0]), Qobj(res[1]) return res
def test_excited(self): """ PIQS: Test the calculation of the totally excited state density matrix. The matrix has size (O(N^2), O(N^2)) in Dicke basis ('dicke'). The matrix has size (2^N, 2^N) in the uncoupled basis ('uncoupled'). """ N = 3 true_state = np.zeros((6, 6)) true_state[0, 0] = 1 true_state = Qobj(true_state) test_state = excited(N) assert_equal(test_state, true_state) N = 4 true_state = np.zeros((9, 9)) true_state[0, 0] = 1 true_state = Qobj(true_state) test_state = excited(N) assert_equal(test_state, true_state) # uncoupled test_state_uncoupled = excited(2, basis="uncoupled") assert_array_equal(test_state_uncoupled.dims, [[2, 2], [2, 2]]) assert_array_equal(test_state_uncoupled.shape, (4, 4)) assert_almost_equal(test_state_uncoupled.full()[0, 0], 1+0j)
def rand_unitary(N, density=0.75, dims=None): """Creates a random NxN sparse unitary quantum object. Uses :math:`\exp(-iH)` where H is a randomly generated Hermitian operator. Parameters ---------- N : int Shape of output quantum operator. density : float Density between [0,1] of output Unitary operator. Returns ------- oper : qobj NxN Unitary quantum operator. Other Parameters ---------------- dims : list Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[N]]. """ if dims: _check_dims(dims, N, N) U = (-1.0j * rand_herm(N, density)).expm() if dims: return Qobj(U, dims=dims, shape=[N, N]) else: return Qobj(U)
def test_extract(self): ados = self.mk_ados([2, 3], max_depth=2) rho, ado_soln = self.mk_rho_and_soln(ados, [[2], [2]]) ado_state = HierarchyADOsState(rho, ados, ado_soln) ado_state.extract((0, 0)) == rho ado_state.extract(0) == rho ado_state.extract((0, 1)) == Qobj(ado_soln[1, :], dims=rho.dims) ado_state.extract(1) == Qobj(ado_soln[1, :], dims=rho.dims)
def __init__(self, matrix: Qobj): matrix.isherm = True self.H = matrix self.N = N_from_qobj(matrix) self.npH = qobj_to_np(self.H) self.min_eigenvalue = matrix.eigenenergies()[0] self.classical_psi0 = min(range(2**self.N), key=lambda i: expected_value(self.npH, classical_state(self.N, i))) self.classical_psi0_bitstring = ('{:0' + str(self.N) + 'b}').format(self.classical_psi0)
def test_lindbladian(self): """ PIQS: Test the generation of the Lindbladian matrix. """ N = 1 gCE = 0.5 gCD = 0.5 gCP = 0.5 gE = 0.1 gD = 0.1 gP = 0.1 system = Dicke(N=N, emission=gE, pumping=gP, dephasing=gD, collective_emission=gCE, collective_pumping=gCP, collective_dephasing=gCD) lindbladian = system.lindbladian() Ldata = np.zeros((4, 4), dtype="complex") Ldata[0] = [-0.6, 0, 0, 0.6] Ldata[1] = [0, -0.9, 0, 0] Ldata[2] = [0, 0, -0.9, 0] Ldata[3] = [0.6, 0, 0, -0.6] lindbladian_correct = Qobj(Ldata, dims=[[[2], [2]], [[2], [2]]], shape=(4, 4)) assert_array_almost_equal(lindbladian.data.toarray(), Ldata) N = 2 gCE = 0.5 gCD = 0.5 gCP = 0.5 gE = 0.1 gD = 0.1 gP = 0.1 system = Dicke(N=N, emission=gE, pumping=gP, dephasing=gD, collective_emission=gCE, collective_pumping=gCP, collective_dephasing=gCD) lindbladian = system.lindbladian() Ldata = np.zeros((16, 16), dtype="complex") Ldata[0][0], Ldata[0][5], Ldata[0][15] = -1.2, 1.1, 0.1 Ldata[1, 1], Ldata[1, 6] = -2, 1.1 Ldata[2, 2] = -2.2999999999999998 Ldata[4, 4], Ldata[4, 9] = -2, 1.1 Ldata[5, 0], Ldata[5, 5], Ldata[5, 10], Ldata[5, 15] = (1.1, -2.25, 1.1, 0.05) Ldata[6, 1], Ldata[6, 6] = 1.1, -2 Ldata[8, 8] = -2.2999999999999998 Ldata[9, 4], Ldata[9, 9] = 1.1, -2 Ldata[10, 5], Ldata[10, 10], Ldata[10, 15] = 1.1, -1.2, 0.1 Ldata[15, 0], Ldata[15, 5], Ldata[15, 10], Ldata[15, 15] = (0.1, 0.05, 0.1, -0.25) lindbladian_correct = Qobj(Ldata, dims=[[[4], [4]], [[4], [4]]], shape=(16, 16)) assert_array_almost_equal(lindbladian.data.toarray(), Ldata)
def qobj_to_np(obj: Qobj) -> np.ndarray: if obj.isket: return obj.full().flatten() elif obj.isbra: return obj.full().flatten().conj() elif obj.isoper: return obj.full() else: raise ValueError("Qobj is not ket or bra")
def split_AB_blocks(m, block_info, n, as_qobj=False): """Split a sparse matrix into two block-diagonal matrices Args: m (scipy.sparse.spmatrix): The sparse matrix to operator on block_info (np.ndarray): Array of block size information n (int): dimension of `m` Returns: tuple[list[scipy.sparse.csr_matrix]]: a tuple of two "buckets" A, B ... Assumptions on `m`: - contains data only in the upper triangle - consists of rectangular blocks sitting on the diagonal TODO """ if isinstance(m, Qobj): m = m.data assert (scipy.sparse.triu(m) - m).nnz == 0 m_coo = m.tocoo() assert np.all(np.sort(m_coo.row) == m_coo.row) assert block_info.dtype == np.int32 data, row, col, nnz = m_coo.data, m_coo.row, m_coo.col, m_coo.nnz block_info = iter([n for n in block_info if n != 0]) selector = 0 n_row_limit = next(block_info) - 1 # how far can we look ahead? AB = {0: [], 1: []} block_data, block_row, block_col = [], [], [] for k in range(nnz): i = row[k] j = col[k] v = data[k] if i > n_row_limit: n_row_limit += next(block_info) selector = (selector + 1) % 2 # toggle 0↔1 if len(block_data) > 0: block = scipy.sparse.coo_matrix( (block_data, (block_row, block_col)), shape=(n, n) ) if as_qobj: block = Qobj(block) AB[selector].append(block) block_data, block_row, block_col = [], [], [] block_data.append(v) block_row.append(i) block_col.append(j) if len(block_data) > 0: selector = (selector + 1) % 2 # toggle 0↔1 block = scipy.sparse.coo_matrix( (block_data, (block_row, block_col)), shape=(n, n) ) if as_qobj: block = Qobj(block) AB[selector].append(block) return AB[0], AB[1]
def test_Transformation4(): "Transform 10-level imag to eigenbasis and back" N = 10 H1 = Qobj(1j * (0.5 - scipy.rand(N, N))) H1 = H1 + H1.dag() evals, ekets = H1.eigenstates() Heb = H1.transform(ekets) # eigenbasis (should be diagonal) H2 = Heb.transform(ekets, True) # back to original basis assert_equal((H1 - H2).norm() < 1e-6, True)
def get_dispersion(self, knpoints=0): """ Returns dispersion relationship for the lattice with the specified number of unit cells with a k array and a band energy array. Returns ------- knxa : np.array knxA[j][0] is the jth good Quantum number k. val_kns : np.array val_kns[j][:] is the array of band energies of the jth band good at all the good Quantum numbers of k. """ # The _k_space_calculations() function is not used for get_dispersion # because we calculate the infinite crystal dispersion in # plot_dispersion using this coode and we do not want to calculate # all the eigen-values, eigenvectors of the bulk Hamiltonian for too # many points, as is done in the _k_space_calculations() function. if self.period_bnd_cond_x == 0: raise Exception("The lattice is not periodic.") if knpoints == 0: knpoints = self.num_cell a = 1 # The unit cell length is always considered 1 kn_start = 0 kn_end = 2*np.pi/a val_kns = np.zeros((self._length_of_unit_cell, knpoints), dtype=float) knxA = np.zeros((knpoints, 1), dtype=float) G0_H = self._H_intra # knxA = np.roll(knxA, np.floor_divide(knpoints, 2)) for ks in range(knpoints): knx = kn_start + (ks*(kn_end-kn_start)/knpoints) if knx >= np.pi: knxA[ks, 0] = knx - 2 * np.pi else: knxA[ks, 0] = knx knxA = np.roll(knxA, np.floor_divide(knpoints, 2)) for ks in range(knpoints): kx = knxA[ks, 0] H_ka = G0_H k_cos = [[kx]] for m in range(len(self._H_inter_list)): r_cos = self._inter_vec_list[m] kr_dotted = np.dot(k_cos, r_cos) H_int = self._H_inter_list[m]*np.exp(kr_dotted*1j)[0] H_ka = H_ka + H_int + H_int.dag() H_k = csr_matrix(H_ka) qH_k = Qobj(H_k) (vals, veks) = qH_k.eigenstates() val_kns[:, ks] = vals[:] return (knxA, val_kns)
def solve(self, rho0, tlist, options=None, progress=None): """ Solve the Hierarchy equations of motion for the given initial density matrix and time. """ if options is None: options = Options() output = Result() output.solver = "hsolve" output.times = tlist output.states = [] output.states.append(Qobj(rho0)) dt = np.diff(tlist) rho_he = np.zeros(self.hshape, dtype=np.complex) rho_he[0] = rho0.full().ravel("F") rho_he = rho_he.flatten() self.rhs() L_helems = self.L_helems.asformat("csr") r = ode(cy_ode_rhs) r.set_f_params(L_helems.data, L_helems.indices, L_helems.indptr) r.set_integrator( "zvode", method=options.method, order=options.order, atol=options.atol, rtol=options.rtol, nsteps=options.nsteps, first_step=options.first_step, min_step=options.min_step, max_step=options.max_step, ) r.set_initial_value(rho_he, tlist[0]) dt = np.diff(tlist) n_tsteps = len(tlist) if progress: bar = progress(total=n_tsteps - 1) for t_idx, t in enumerate(tlist): if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) r1 = r.y.reshape(self.hshape) r0 = r1[0].reshape(self.N, self.N).T output.states.append(Qobj(r0)) r_heom = r.y.reshape(self.hshape) self.full_hierarchy.append(r_heom) if progress: bar.update() return output
def __init__(self,N): self.N = N self.dims = [2]*self.N self.ansatzes = [] self.structures = [] self.para = [] self.qc = None # Generate circuit on demand self.statein = Qobj() self.stateout = Qobj()
def sync(self, state=None, t_bath=None, y_0=None, hu=None, htl=None, e_ops=None, args=None): """ sync data into kernel environment. """ assert self.compiled self.synced = False self._validate_sync_args(state, t_bath, y_0, hu, htl, e_ops) if state is not None: self.state = npmat_manylike(self.system.h0, state) if hu is not None: self.hu = npmat_manylike(self.system.h0, hu) if t_bath is not None: self.t_bath = vectorize(t_bath, dtype=QOP.T_FLOAT) assert np.all(self.t_bath >= 0) if y_0 is not None: self.y_0 = vectorize(y_0, dtype=QOP.T_FLOAT) assert np.all(self.y_0 >= 0) if args is not None: self.args = args #assert np.all(self.args >= 0) # XXX todo make observables listable if self.n_e_ops > 0 and e_ops is not None: self.e_ops = npmat_manylike(self.system.h0, e_ops) assert len(self.e_ops) == self.n_e_ops self.q_e_ops = [Qobj(e) for e in self.e_ops] # XXX todo make time dependent operators listable if self.n_htl > 0 and htl is not None: self.htl = list(htl) assert len(self.htl) == self.n_htl self.q_htl = [[Qobj(sqmat(ht[0])), ht[1]] for ht in self.htl] # normalize all buffers such that length are the same. tb = self.t_bath if self.t_bath is not None else [0.0] self.q_state, \ self.q_hu, \ self.n_dst, \ self.r_y_0 = self._sync_fill_up( q_state = [Qobj(s) for s in self.state], q_hu = [Qobj(hu) for hu in self.hu], n_dst = [boson_stat(t) for t in tb], r_y_0 = self.y_0 if self.y_0 is not None else np.array([0.0], dtype=QOP.T_FLOAT)) self.synced = True
def sweep_diagonalize_full_Hamiltonian(self, sweep_variable): """Diagonalizes the coupled circuit and resonator Hamiltonian. Args: sweep_variable (str): Name of the parameter to sweep. Returns: None. """ a = tensor(destroy(sim_settings.NFOCK), qeye(self.keig)) # annihilation operator adg = a.dag() # creation operator # Create the resonator Hamiltonian in the resonator + qubit basis. H_resonator = self.f_resonator * (adg * a) sweep_vector = getattr(self, sweep_variable + '_ls') sweep_evals = [] sweep_idx = 0 for sweep_point in tqdm(sweep_vector): # Create the qubit Hamiltonian in the resonator + qubit basis. H = Qobj(diags(self.evals[sweep_idx, :])) H_qubit = tensor(qeye(sim_settings.NFOCK), H) # Create the coupling Hamiltonian in the resonator + qubit basis. H_coupling = tensor(qeye(sim_settings.NFOCK), Qobj(self.g[sweep_idx, :, :])) * (a + adg) # Create the full Hamiltonian. H_full = H_qubit + H_resonator + H_coupling # Diagonalize the full Hamiltonian. evals, _ = H_full.eigenstates(sparse=False, sort='low', eigvals=self.keig * sim_settings.NFOCK, tol=sim_settings.DIAG_TOL) evals = evals - evals[0] sweep_evals.append( dict(evals_full=evals)) # Append the result to the list. sweep_idx += 1 # Convert the list to the attribute. setattr(self, 'evals_full', self.extract_results('evals_full', sweep_evals)) return
def test_basis(self): """ lattice: Test the method Lattice1d.basis(). """ lattice_3242 = Lattice1d(num_cell=3, boundary="periodic", cell_num_site=2, cell_site_dof=[4, 2]) psi0 = lattice_3242.basis(1, 0, [2, 1]) psi0dag_a = np.zeros((1, 48), dtype=complex) psi0dag_a[0, 21] = 1 psi0dag = Qobj(psi0dag_a, dims=[[1, 1, 1, 1], [3, 2, 4, 2]]) assert_(psi0 == psi0dag.dag())
def test_ghz(self): """ PIQS: Test the calculation of the density matrix of the GHZ state. PIQS: Test for N = 2 in the 'dicke' and in the 'uncoupled' basis. """ ghz_dicke = Qobj([[0.5, 0, 0.5, 0], [0, 0, 0, 0], [0.5, 0, 0.5, 0], [0, 0, 0, 0]]) ghz_uncoupled = Qobj([[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]]) ghz_uncoupled.dims = [[2, 2], [2, 2]] assert_equal(ghz(2), ghz_dicke) assert_equal(ghz(2, "uncoupled"), ghz_uncoupled)
def test_lindbladian_dims(self): """ PIQS: Test the calculation of the lindbladian matrix. """ true_L = [[-4, 0, 0, 3], [0, -3.54999995, 0, 0], [0, 0, -3.54999995, 0], [4, 0, 0, -3]] true_L = Qobj(true_L) true_L.dims = [[[2], [2]], [[2], [2]]] N = 1 test_dicke = _Dicke(N=N, pumping=1, collective_pumping=2, emission=1, collective_emission=3, dephasing=0.1) test_L = test_dicke.lindbladian() assert_array_almost_equal(test_L.full(), true_L.full()) assert_array_equal(test_L.dims, true_L.dims)
def rand_herm(N,density=0.75,dims=None): """Creates a random NxN sparse Hermitian quantum object. Uses :math:`H=X+X^{+}` where :math:`X` is a randomly generated quantum operator with a given `density`. Parameters ---------- N : int Shape of output quantum operator. density : float Density etween [0,1] of output Hermitian operator. Returns ------- oper : qobj NxN Hermitian quantum operator. Other Parameters ---------------- dims : list Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[N]]. """ if dims: _check_dims(dims,N,N) # to get appropriate density of output # Hermitian operator must convert via: herm_density=2.0*arcsin(density)/pi X = sp.rand(N,N,herm_density,format='csr') X.data=X.data-0.5 Y=X.copy() Y.data=1.0j*np.random.random(len(X.data))-(0.5+0.5j) X=X+Y X=Qobj(X) if dims: return Qobj((X+X.dag())/2.0,dims=dims,shape=[N,N]) else: return Qobj((X+X.dag())/2.0)
def floquet_master_equation_tensor(Alist, f_energies): """ Construct a tensor that represents the master equation in the floquet basis (with constant Hamiltonian and collapse operators). Simplest RWA approximation [Grifoni et al, Phys.Rep. 304 229 (1998)] """ if isinstance(Alist, list): # Alist can be a list of rate matrices corresponding # to different operators that couple to the environment N, M = shape(Alist[0]) else: # or a simple rate matrix, in which case we put it in a list Alist = [Alist] N, M = shape(Alist[0]) R = Qobj(scipy.sparse.csr_matrix((N*N,N*N)), [[N,N], [N,N]], [N*N,N*N]) R.data=R.data.tolil() for I in range(N*N): a,b = vec2mat_index(N, I) for J in range(N*N): c,d = vec2mat_index(N, J) R.data[I,J] = - 1.0j * (f_energies[a]-f_energies[b]) * (a == c) * (b == d) for A in Alist: s1 = s2 = 0 for n in range(N): s1 += A[a,n] * (n == c) * (n == d) - A[n,a] * (a == c) * (a == d) s2 += (A[n,a] + A[n,b]) * (a == c) * (b == d) dR = (a == b) * s1 - 0.5 * (1 - (a == b)) * s2 if dR != 0.0: R.data[I,J] += dR R.data=R.data.tocsr() return R
def _compute_prop_grad(self, k, j, compute_prop=True): """ Calculate the gradient of propagator wrt the control amplitude in the timeslot. Returns: [prop], prop_grad """ dyn = self.parent dyn._ensure_decomp_curr(k) if compute_prop: prop = self._compute_propagator(k) if dyn.oper_dtype == Qobj: # put control dyn_gen in combined dg diagonal basis cdg = (dyn._get_dyn_gen_eigenvectors_adj(k)* dyn._get_phased_ctrl_dyn_gen(k, j)* dyn._dyn_gen_eigenvectors[k]) # multiply (elementwise) by timeslice and factor matrix cdg = Qobj(np.multiply(cdg.full()*dyn.tau[k], dyn._dyn_gen_factormatrix[k]), dims=dyn.dyn_dims) # Return to canonical basis prop_grad = (dyn._dyn_gen_eigenvectors[k]*cdg* dyn._get_dyn_gen_eigenvectors_adj(k)) else: # put control dyn_gen in combined dg diagonal basis cdg = dyn._get_dyn_gen_eigenvectors_adj(k).dot( dyn._get_phased_ctrl_dyn_gen(k, j)).dot( dyn._dyn_gen_eigenvectors[k]) # multiply (elementwise) by timeslice and factor matrix cdg = np.multiply(cdg*dyn.tau[k], dyn._dyn_gen_factormatrix[k]) # Return to canonical basis prop_grad = dyn._dyn_gen_eigenvectors[k].dot(cdg).dot( dyn._get_dyn_gen_eigenvectors_adj(k)) if compute_prop: return prop, prop_grad else: return prop_grad
def spre(A): """Superoperator formed from pre-multiplication by operator A. Parameters ---------- A : qobj Quantum operator for pre-multiplication. Returns -------- super :qobj Superoperator formed from input quantum object. """ if not isoper(A): raise TypeError("Input is not a quantum object") d = A.dims[1] S = Qobj() S.dims = [[A.dims[0][:], d[:]], [A.dims[1][:], d[:]]] S.shape = [prod(S.dims[0][0]) * prod(S.dims[0][1]), prod(S.dims[1][0]) * prod(S.dims[1][1])] S.data = sp.kron(sp.identity(prod(d)), A.data, format="csr") return S
def spost(A): """Superoperator formed from post-multiplication by operator A Parameters ---------- A : qobj Quantum operator for post multiplication. Returns ------- super : qobj Superoperator formed from input qauntum object. """ if not isoper(A): raise TypeError('Input is not a quantum object') d=A.dims[0] S=Qobj() S.dims=[[d[:],A.dims[1][:]],[d[:],A.dims[0][:]]] S.shape=[prod(S.dims[0][0])*prod(S.dims[0][1]),prod(S.dims[1][0])*prod(S.dims[1][1])] S.data=sp.kron(A.data.T,sp.identity(prod(d))) return Qobj(S)
def probabilities(self, out_state, sort=False): state_copy = out_state.full().copy() length = state_copy.shape[0]-len(self.state.indistinguishable_states) new_state = np.zeros((length,1),dtype=np.complex128) new_dict = {} for i,(label,dim) in enumerate(self.state.reduced_label_map.iteritems()): reverse_label = "_".join(label.split('_')[::-1]) if (label, reverse_label) in self.state.indistinguishable_states.keys(): if (label, reverse_label) not in new_dict.keys(): new_key = (label, reverse_label) new_dict[new_key] = np.complex128(0) dims_to_add = self.state.indistinguishable_states[(label, reverse_label)] for dim in dims_to_add: new_dict[new_key] += state_copy[dim] else: if (label) not in new_dict.keys(): if (reverse_label) not in new_dict.keys(): if (label, reverse_label) not in self.state.indistinguishable_states.keys(): if (reverse_label, label) not in self.state.indistinguishable_states.keys(): new_dict[(label)] = state_copy[dim] new_state = np.array(new_dict.values()) new_qstate = Qobj(new_state).unit() prob_array = new_qstate.conj().full()*new_qstate.full() probs = {lab: prob for lab,prob in zip(new_dict.keys(), prob_array)} probs = {lab: prob.real.tolist()[0] for lab,prob in probs.iteritems() } # Check that probabilities add to one np.testing.assert_almost_equal(sum(probs.values()),1.0,decimal=5) # Return list if sort=True, else return dict if sort: return sorted(probs.items(), key=lambda x:x[1]) else: return probs
def liouvillian_fast(H, c_op_list): """Assembles the Liouvillian superoperator from a Hamiltonian and a ``list`` of collapse operators. Like liouvillian, but with an experimental implementation which avoids creating extra Qobj instances, which can be advantageous for large systems. Parameters ---------- H : qobj System Hamiltonian. c_op_list : array_like A ``list`` or ``array`` of collpase operators. Returns ------- L : qobj Louvillian superoperator. .. note:: Experimental. """ d = H.dims[1] L = Qobj() L.dims = [[H.dims[0][:], d[:]], [H.dims[1][:], d[:]]] L.shape = [prod(L.dims[0][0]) * prod(L.dims[0][1]), prod(L.dims[1][0]) * prod(L.dims[1][1])] L.data = -1j * ( sp.kron(sp.identity(prod(d)), H.data, format="csr") - sp.kron(H.data.T, sp.identity(prod(d)), format="csr") ) n_op = len(c_op_list) for m in range(0, n_op): L.data = L.data + sp.kron(sp.identity(prod(d)), c_op_list[m].data, format="csr") * sp.kron( c_op_list[m].data.T.conj().T, sp.identity(prod(d)), format="csr" ) cdc = c_op_list[m].data.T.conj() * c_op_list[m].data L.data = L.data - 0.5 * sp.kron(sp.identity(prod(d)), cdc, format="csr") L.data = L.data - 0.5 * sp.kron(cdc.T, sp.identity(prod(d)), format="csr") return L
def test_liouvillian(self): """ PIQS: Test the calculation of the liouvillian matrix. """ true_L = [[-4, 0, 0, 3], [0, -3.54999995, 0, 0], [0, 0, -3.54999995, 0], [4, 0, 0, -3]] true_L = Qobj(true_L) true_L.dims = [[[2], [2]], [[2], [2]]] true_H = [[1. + 0.j, 1. + 0.j], [1. + 0.j, -1. + 0.j]] true_H = Qobj(true_H) true_H.dims = [[[2], [2]]] true_liouvillian = [[-4, -1.j, 1.j, 3], [-1.j, -3.54999995 + 2.j, 0, 1.j], [1.j, 0, -3.54999995 - 2.j, -1.j], [4, +1.j, -1.j, -3]] true_liouvillian = Qobj(true_liouvillian) true_liouvillian.dims = [[[2], [2]], [[2], [2]]] N = 1 test_piqs = Dicke(hamiltonian=sigmaz() + sigmax(), N=N, pumping=1, collective_pumping=2, emission=1, collective_emission=3, dephasing=0.1) test_liouvillian = test_piqs.liouvillian() test_hamiltonian = test_piqs.hamiltonian assert_array_almost_equal( test_liouvillian.full(), true_liouvillian.full()) assert_array_almost_equal(test_hamiltonian.full(), true_H.full()) assert_array_equal(test_liouvillian.dims, test_liouvillian.dims) # no Hamiltonian test_piqs = Dicke(N=N, pumping=1, collective_pumping=2, emission=1, collective_emission=3, dephasing=0.1) liouv = test_piqs.liouvillian() lindblad = test_piqs.lindbladian() assert_equal(liouv, lindblad)
def generic_ode_solve(r, psi0, tlist, expt_ops, opt, state_vectorize): """ Internal function for solving ODEs. """ # # prepare output array # n_tsteps = len(tlist) dt = tlist[1]-tlist[0] output = Odedata() output.times = tlist if isinstance(expt_ops, FunctionType): n_expt_op = 0 expt_callback = True elif isinstance(expt_ops, list): n_expt_op = len(expt_ops) expt_callback = False if n_expt_op == 0: output.states = [] else: output.expect = [] output.num_expect = n_expt_op for op in expt_ops: if op.isherm and psi0.isherm: output.expect.append(zeros(n_tsteps)) else: output.expect.append(zeros(n_tsteps,dtype=complex)) else: raise TypeError("Expectation parameter must be a list or a function") # # start evolution # psi = Qobj(psi0) t_idx = 0 for t in tlist: if not r.successful(): break; psi.data = state_vectorize(r.y) if expt_callback: # use callback method expt_ops(t, Qobj(psi)) else: # calculate all the expectation values, or output rho if no operators if n_expt_op == 0: output.states.append(Qobj(psi)) # copy psi/rho else: for m in range(0, n_expt_op): output.expect[m][t_idx] = expect(expt_ops[m], psi) r.integrate(r.t + dt) t_idx += 1 if not opt.rhs_reuse and odeconfig.tdname != None: try: os.remove(odeconfig.tdname+".pyx") except: print('Error removing '+str(odeconfig.tdname)+".pyx file") pass return output
def test_lindbladian(self): """ Optimise pulse for amplitude damping channel with Lindbladian dyn assert that fidelity error is below threshold """ Sx = sigmax() Sz = sigmaz() Si = identity(2) Sd = Qobj(np.array([[0, 1], [0, 0]])) Sm = Qobj(np.array([[0, 0], [1, 0]])) Sd_m = Qobj(np.array([[1, 0], [0, 0]])) gamma = 0.1 L0_Ad = gamma*(2*tensor(Sm, Sd.trans()) - (tensor(Sd_m, Si) + tensor(Si, Sd_m.trans()))) LC_x = -1j*(tensor(Sx, Si) - tensor(Si, Sx)) LC_z = -1j*(tensor(Sz, Si) - tensor(Si, Sz)) drift = L0_Ad ctrls = [LC_z, LC_x] n_ctrls = len(ctrls) initial = tensor(Si, Si) had_gate = hadamard_transform(1) target_DP = tensor(had_gate, had_gate) n_ts = 10 evo_time = 5 result = cpo.optimize_pulse(drift, ctrls, initial, target_DP, n_ts, evo_time, fid_err_targ=1e-3, max_iter=200, init_pulse_type='LIN', gen_stats=True) assert_(result.fid_err < 0.1, msg="Fidelity higher than expected") # Repeat with Qobj propagation result = cpo.optimize_pulse(drift, ctrls, initial, target_DP, n_ts, evo_time, fid_err_targ=1e-3, max_iter=200, init_pulse_type='LIN', dyn_params={'oper_dtype':Qobj}, gen_stats=True) assert_(result.fid_err < 0.1, msg="Fidelity higher than expected (Qobj propagation)") # Check same result is achieved using the create objects method optim = cpo.create_pulse_optimizer(drift, ctrls, initial, target_DP, n_ts, evo_time, fid_err_targ=1e-3, init_pulse_type='LIN', gen_stats=True) dyn = optim.dynamics p_gen = optim.pulse_generator init_amps = np.zeros([n_ts, n_ctrls]) for j in range(n_ctrls): init_amps[:, j] = p_gen.gen_pulse() dyn.initialize_controls(init_amps) # Check the exact gradient func = optim.fid_err_func_wrapper grad = optim.fid_err_grad_wrapper x0 = dyn.ctrl_amps.flatten() grad_diff = check_grad(func, grad, x0) assert_almost_equal(grad_diff, 0.0, decimal=7, err_msg="Frechet gradient outside tolerance") result2 = optim.run_optimization() assert_almost_equal(result.fid_err, result2.fid_err, decimal=3, err_msg="Direct and indirect methods produce " "different results for ADC")
def floquet_markov_mesolve(R, ekets, rho0, tlist, e_ops, opt=None): """ Solve the dynamics for the system using the Floquet-Markov master equation. """ if opt == None: opt = Odeoptions() if opt.tidy: R.tidyup() # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # prepare output array # n_tsteps = len(tlist) dt = tlist[1]-tlist[0] output = Odedata() output.times = tlist if isinstance(e_ops, FunctionType): n_expt_op = 0 expt_callback = True elif isinstance(e_ops, list): n_expt_op = len(e_ops) expt_callback = False if n_expt_op == 0: output.states = [] else: output.expect = [] output.num_expect = n_expt_op for op in e_ops: if op.isherm: output.expect.append(zeros(n_tsteps)) else: output.expect.append(zeros(n_tsteps,dtype=complex)) else: raise TypeError("Expectation parameter must be a list or a function") # # transform the initial density matrix and the e_ops opterators to the # eigenbasis: from computational basis to the floquet basis # if ekets != None: rho0 = rho0.transform(ekets, True) if isinstance(e_ops, list): for n in arange(len(e_ops)): # not working e_ops[n] = e_ops[n].transform(ekets) # # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(cyq_ode_rhs) r.set_f_params(R.data.data, R.data.indices, R.data.indptr) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # start evolution # rho = Qobj(rho0) t_idx = 0 for t in tlist: if not r.successful(): break; rho.data = vec2mat(r.y) if expt_callback: # use callback method e_ops(t, Qobj(rho)) else: # calculate all the expectation values, or output rho if no operators if n_expt_op == 0: output.states.append(Qobj(rho)) # copy psi/rho else: for m in range(0, n_expt_op): output.expect[m][t_idx] = expect(e_ops[m], rho) # basis OK? r.integrate(r.t + dt) t_idx += 1 return output
import qutip.logging_utils as logging logger = logging.get_logger() #QuTiP control modules import qutip.control.pulseoptim as cpo example_name = 'Lindblad' log_level = logging.INFO # **************************************************************** # Define the physics of the problem Sx = sigmax() Sy = sigmay() Sz = sigmaz() Si = identity(2) Sd = Qobj(np.array([[0, 1], [0, 0]])) Sm = Qobj(np.array([[0, 0], [1, 0]])) Sd_m = Qobj(np.array([[1, 0], [0, 0]])) Sm_d = Qobj(np.array([[0, 0], [0, 1]])) #Amplitude damping# #Damping rate: gamma = 0.1 L0_Ad = gamma*(2*tensor(Sm, Sd.trans()) - (tensor(Sd_m, Si) + tensor(Si, Sd_m.trans()))) #sigma X control LC_x = -1j*(tensor(Sx, Si) - tensor(Si, Sx)) #sigma Y control
def steady(L,maxiter=100,tol=1e-6,method='solve'): """Steady state for the evolution subject to the supplied Louvillian. Parameters ---------- L : qobj Liouvillian superoperator. maxiter : int Maximum number of iterations to perform, default = 100. tol : float Tolerance used by iterative solver, default = 1e-6. method : str Method for solving linear equations. Direct solver 'solve' (default) or iterative biconjugate gradient method 'bicg'. Returns -------- ket : qobj Ket vector for steady state. Notes ----- Uses the inverse power method. See any Linear Algebra book with an iterative methods section. """ eps=finfo(float).eps if (not isoper(L)) & (not issuper(L)): raise TypeError('Steady states can only be found for operators or superoperators.') rhoss=Qobj() sflag=issuper(L) if sflag: rhoss.dims=L.dims[0] rhoss.shape=[prod(rhoss.dims[0]),prod(rhoss.dims[1])] else: rhoss.dims=[L.dims[0],1] rhoss.shape=[prod(rhoss.dims[0]),1] n=prod(rhoss.shape) L1=L.data+eps*_sp_inf_norm(L)*sp.eye(n,n,format='csr') v=randn(n,1) it=0 while (la.norm(L.data*v,np.inf)>tol) and (it<maxiter): if method=='bicg': v,check=bicg(L1,v,tol=tol) else: v=spsolve(L1,v,use_umfpack=False) v=v/la.norm(v,np.inf) it+=1 if it>=maxiter: raise ValueError('Failed to find steady state after ' + str(maxiter) +' iterations') #normalise according to type of problem if sflag: trow=sp.eye(rhoss.shape[0],rhoss.shape[0],format='lil') trow=trow.reshape((1,n)).tocsr() data=v/sum(trow.dot(v)) else: data=data/la.norm(v) data=reshape(data,(rhoss.shape[0],rhoss.shape[1])).T data=sp.csr_matrix(data) rhoss.data=0.5*(data+data.conj().T) #data=sp.triu(data,format='csr')#take only upper triangle #rhoss.data=0.5*sp.eye(rhoss.shape[0],rhoss.shape[1],format='csr')*(data+data.conj().T) #output should be hermitian, but not guarenteed using iterative meth if qset.auto_tidyup: return Qobj(rhoss).tidyup() else: return Qobj(rhoss)
def bloch_redfield_solve(R, ekets, rho0, tlist, e_ops=[], opt=None): """ Evolve the ODEs defined by Bloch-Redfeild master equation. """ if opt == None: opt = Odeoptions() opt.nsteps = 2500 # if opt.tidy: R.tidyup() # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = rho0 * rho0.dag() # # prepare output array # m n_e_ops = len(e_ops) n_tsteps = len(tlist) dt = tlist[1]-tlist[0] if n_e_ops == 0: result_list = [] else: result_list = [] for op in e_ops: if op.isherm and rho0.isherm: result_list.append(zeros(n_tsteps)) else: result_list.append(zeros(n_tsteps,dtype=complex)) # # transform the initial density matrix and the e_ops opterators to the # eigenbasis # if ekets != None: rho0 = rho0.transform(ekets) for n in arange(len(e_ops)): e_ops[n] = e_ops[n].transform(ekets, False) # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(cyq_ode_rhs) r.set_f_params(R.data.data, R.data.indices, R.data.indptr) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, #nsteps=opt.nsteps, #first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # start evolution # rho = Qobj(rho0) t_idx = 0 for t in tlist: if not r.successful(): break; rho.data = vec2mat(r.y) # calculate all the expectation values, or output rho if no operators if n_e_ops == 0: result_list.append(Qobj(rho)) else: for m in range(0, n_e_ops): result_list[m][t_idx] = expect(e_ops[m], rho) r.integrate(r.t + dt) t_idx += 1 return result_list