def _ptrace(rho, sel): """ Private function calculating the partial trace. """ if isinstance(sel, int): sel = np.array([sel]) else: sel = np.asarray(sel) if (sel < 0).any() or (sel >= len(rho.dims[0])).any(): raise TypeError("Invalid selection index in ptrace.") drho = rho.dims[0] N = np.prod(drho) M = np.prod(np.asarray(drho).take(sel)) if np.prod(rho.dims[1]) == 1: rho = rho * rho.dag() perm = sp.lil_matrix((M * M, N * N)) # all elements in range(len(drho)) not in sel set rest = np.setdiff1d(np.arange(len(drho)), sel) ilistsel = _select(sel, drho) indsel = _list2ind(ilistsel, drho) ilistrest = _select(rest, drho) indrest = _list2ind(ilistrest, drho) irest = (indrest - 1) * N + indrest - 2 # Possibly use parfor here if M > some value ? # We have to initialise like this to get a numpy array of lists rather than # a standard numpy 2d array. Scipy >= 1.5 requires that we do this, rather # than just pass a 2d array, and scipy < 1.5 will accept it (because it's # actually the correct format). perm.rows = np.empty((M * M,), dtype=object) perm.data = np.empty((M * M,), dtype=object) for m in range(M * M): perm.rows[m] = list((irest + (indsel[int(np.floor(m / M))] - 1)*N + indsel[int(np.mod(m, M))]).T[0]) perm.data[m] = [1.0] * len(perm.rows[m]) perm = perm.tocsr() rhdata = perm * sp_reshape(rho.data, (np.prod(rho.shape), 1)) rho1_data = sp_reshape(rhdata, (M, M)) dims_kept0 = np.asarray(rho.dims[0]).take(sel) dims_kept1 = np.asarray(rho.dims[0]).take(sel) rho1_dims = [dims_kept0.tolist(), dims_kept1.tolist()] rho1_shape = [np.prod(dims_kept0), np.prod(dims_kept1)] return rho1_data, rho1_dims, rho1_shape
def operator_to_vector(op): """ Create a vector representation given a quantum operator in matrix form. The passed object should have a ``Qobj.type`` of 'oper' or 'super'; this function is not designed for general-purpose matrix reshaping. Parameters ---------- op : Qobj or QobjEvo Quantum operator in matrix form. This must have a type of 'oper' or 'super'. Returns ------- Qobj or QobjEvo The same object, but re-cast into a column-stacked-vector form of type 'operator-ket'. The output is the same type as the passed object. """ if isinstance(op, QobjEvo): return op.apply(operator_to_vector) if not (op.isoper or op.issuper): raise ValueError("only valid for operator matrices") size = op.shape[0] * op.shape[1] return Qobj( sp_reshape(op.data.T, (size, 1)), dims=[op.dims, [1]], shape=(size, 1), type='operator-ket', copy=False, )
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. The passed object should have a ``Qobj.type`` of 'operator-ket'; this function is not designed for general-purpose matrix reshaping. Parameters ---------- op : Qobj or QobjEvo Quantum operator in column-stacked-vector form. This must have a type of 'operator-ket'. Returns ------- Qobj or QobjEvo The same object, but re-cast into "standard" operator form. The output is the same type as the passed object. """ if isinstance(op, QobjEvo): return op.apply(vector_to_operator) if not op.isoperket: raise ValueError( "only valid for operators in column-stacked 'operator-ket' format") # e.g. op.dims = [ [[rows], [cols]], [1]] dims = op.dims[0] shape = (np.prod(dims[0]), np.prod(dims[1])) return Qobj( sp_reshape(op.data.T, shape[::-1]).T, dims=dims, shape=shape, copy=False, )
def operator_to_vector(op): """ Create a vector representation of a quantum operator given the matrix representation. """ q = Qobj() q.dims = [op.dims, [1]] q.data = sp_reshape(op.data.T, (np.prod(op.shape), 1)) return q
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ q = Qobj() q.dims = op.dims[0] q.data = sp_reshape(op.data.H, q.shape) return q
def _ptrace(rho, sel): """ Private function calculating the partial trace. """ if isinstance(sel, int): sel = np.array([sel]) else: sel = np.asarray(sel) if (sel < 0).any() or (sel >= len(rho.dims[0])).any(): raise TypeError("Invalid selection index in ptrace.") drho = rho.dims[0] N = np.prod(drho) M = np.prod(np.asarray(drho).take(sel)) if np.prod(rho.dims[1]) == 1: rho = rho * rho.dag() perm = sp.lil_matrix((M * M, N * N)) # all elements in range(len(drho)) not in sel set rest = np.setdiff1d(np.arange(len(drho)), sel) ilistsel = _select(sel, drho) indsel = _list2ind(ilistsel, drho) ilistrest = _select(rest, drho) indrest = _list2ind(ilistrest, drho) irest = (indrest - 1) * N + indrest - 2 # Possibly use parfor here if M > some value ? perm.rows = np.array( [(irest + (indsel[int(np.floor(m / M))] - 1) * N + indsel[int(np.mod(m, M))]).T[0] for m in range(M ** 2)]) # perm.data = np.ones_like(perm.rows,dtype=int) perm.data = np.ones_like(perm.rows) perm.tocsr() rhdata = perm * sp_reshape(rho.data, (np.prod(rho.shape), 1)) rho1_data = sp_reshape(rhdata, (M, M)) dims_kept0 = np.asarray(rho.dims[0]).take(sel) dims_kept1 = np.asarray(rho.dims[0]).take(sel) rho1_dims = [dims_kept0.tolist(), dims_kept1.tolist()] rho1_shape = [np.prod(dims_kept0), np.prod(dims_kept1)] return rho1_data, rho1_dims, rho1_shape
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ q = Qobj() q.dims = op.dims[0] n = int(np.sqrt(op.shape[0])) q.data = sp_reshape(op.data.T, (n, n)).T return q
def _ptrace(rho, sel): """ Private function calculating the partial trace. """ if isinstance(sel, int): sel = np.array([sel]) else: sel = np.asarray(sel) if (sel < 0).any() or (sel >= len(rho.dims[0])).any(): raise TypeError("Invalid selection index in ptrace.") drho = rho.dims[0] N = np.prod(drho) M = np.prod(np.asarray(drho).take(sel)) if np.prod(rho.dims[1]) == 1: rho = rho * rho.dag() perm = sp.lil_matrix((M * M, N * N)) # all elements in range(len(drho)) not in sel set rest = np.setdiff1d(np.arange(len(drho)), sel) ilistsel = _select(sel, drho) indsel = _list2ind(ilistsel, drho) ilistrest = _select(rest, drho) indrest = _list2ind(ilistrest, drho) irest = (indrest - 1) * N + indrest - 2 # Possibly use parfor here if M > some value ? perm.rows = np.array([(irest + (indsel[int(np.floor(m / M))] - 1) * N + indsel[int(np.mod(m, M))]).T[0] for m in range(M**2)]) # perm.data = np.ones_like(perm.rows,dtype=int) perm.data = np.ones_like(perm.rows) perm = perm.tocsr() rhdata = perm * sp_reshape(rho.data, (np.prod(rho.shape), 1)) rho1_data = sp_reshape(rhdata, (M, M)) dims_kept0 = np.asarray(rho.dims[0]).take(sel) dims_kept1 = np.asarray(rho.dims[0]).take(sel) rho1_dims = [dims_kept0.tolist(), dims_kept1.tolist()] rho1_shape = [np.prod(dims_kept0), np.prod(dims_kept1)] return rho1_data, rho1_dims, rho1_shape
def operator_to_vector(op): """ Create a vector representation of a quantum operator given the matrix representation. """ q = Qobj() q.shape = [prod(op.shape), 1] q.dims = [op.dims, [1]] q.data = sp_reshape(op.data.T, tuple(q.shape)) q.type = 'operator-vector' return q
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ q = Qobj() q.shape = [op.dims[0][0][0], op.dims[0][1][0]] q.dims = op.dims[0] q.data = sp_reshape(op.data.H, tuple(q.shape)) q.type = 'oper' return q
def operator_to_vector(op): """ Create a vector representation of a quantum operator given the matrix representation. """ if isinstance(op, QobjEvo): return op.apply(operator_to_vector) q = Qobj() q.dims = [op.dims, [1]] q.data = sp_reshape(op.data.T, (np.prod(op.shape), 1)) return q
def _steadystate_power(L, maxiter=10, tol=1e-6, itertol=1e-5, use_umfpack=True, verbose=False): """ Inverse power method for steady state solving. """ if verbose: print('Starting iterative power method Solver...') use_solver(assumeSortedIndices=True, useUmfpack=use_umfpack) 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) L = L.data.tocsc() - (tol**2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) if verbose: start_time = time.time() it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v, use_umfpack=use_umfpack) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('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='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if verbose: print('Power solver time: ', time.time() - start_time) if qset.auto_tidyup: return rhoss.tidyup() else: return rhoss
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ if isinstance(op, QobjEvo): return op.apply(vector_to_operator) q = Qobj() # e.g. op.dims = [ [[rows], [cols]], [1]] q.dims = op.dims[0] shape = (np.prod(q.dims[0]), np.prod(q.dims[1])) q.data = sp_reshape(op.data.T, shape[::-1]).T return q
def _steadystate_power(L, maxiter=10, tol=1e-6, itertol=1e-5, use_umfpack=True, verbose=False): """ Inverse power method for steady state solving. """ if verbose: print('Starting iterative power method Solver...') use_solver(assumeSortedIndices=True, useUmfpack=use_umfpack) 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) L = L.data.tocsc() - (tol ** 2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) if verbose: start_time = time.time() it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v, use_umfpack=use_umfpack) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('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='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if verbose: print('Power solver time: ', time.time() - start_time) if qset.auto_tidyup: return rhoss.tidyup() else: return rhoss
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ if settings.debug: print('Starting iterative power method Solver...') tol=ss_args['tol'] maxiter=ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = prod(rhoss.shape) L = L.data.tocsc() - (tol ** 2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('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='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True return rhoss
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ ss_args['info'].pop('weight', None) if settings.debug: logger.debug('Starting iterative inverse-power method solver.') tol = ss_args['tol'] maxiter = ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = L.shape[0] # Build Liouvillian L, perm, perm2, rev_perm, ss_args = _steadystate_power_liouvillian( L, ss_args) orig_nnz = L.nnz # start with all ones as RHS v = np.ones(n, dtype=complex) if ss_args['use_rcm']: v = v[np.ix_(perm2, )] # Do preconditioning if ss_args['M'] is None and ss_args['use_precond'] and \ ss_args['method'] in ['power-gmres', 'power-lgmres', 'power-bicgstab']: ss_args['M'], ss_args = _iterative_precondition( L, int(np.sqrt(n)), ss_args) if ss_args['M'] is None: warnings.warn("Preconditioning failed. Continuing without.", UserWarning) ss_iters = {'iter': 0} def _iter_count(r): ss_iters['iter'] += 1 return _power_start = time.time() # Get LU factors if ss_args['method'] == 'power': lu = splu(L, permc_spec=ss_args['permc_spec'], diag_pivot_thresh=ss_args['diag_pivot_thresh'], options=dict(ILU_MILU=ss_args['ILU_MILU'])) if settings.debug and _scipy_check: L_nnz = lu.L.nnz U_nnz = lu.U.nnz logger.debug('L NNZ: %i ; U NNZ: %i' % (L_nnz, U_nnz)) logger.debug('Fill factor: %f' % ((L_nnz + U_nnz) / orig_nnz)) it = 0 _tol = np.max(ss_args['tol'] / 10, 1e-15) # Should make this user accessible while (la.norm(L * v, np.inf) > tol) and (it < maxiter): if ss_args['method'] == 'power': v = lu.solve(v) elif ss_args['method'] == 'power-gmres': v, check = gmres(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], restart=ss_args['restart'], maxiter=ss_args['maxiter'], callback=_iter_count) elif ss_args['method'] == 'power-lgmres': v, check = lgmres(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], maxiter=ss_args['maxiter'], callback=_iter_count) elif ss_args['method'] == 'power-bicgstab': v, check = bicgstab(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], maxiter=ss_args['maxiter'], callback=_iter_count) else: raise Exception("Invalid iterative solver method.") v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') _power_end = time.time() ss_args['info']['solution_time'] = _power_end - _power_start ss_args['info']['iterations'] = it if ss_args['return_info']: ss_args['info']['residual_norm'] = la.norm(L * v) if settings.debug: logger.debug('Number of iterations: %i' % it) if ss_args['use_rcm']: v = v[np.ix_(rev_perm, )] # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if ss_args['return_info']: return rhoss, ss_args['info'] else: return rhoss
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ ss_args['info'].pop('weight', None) if settings.debug: logger.debug('Starting iterative inverse-power method solver.') tol = ss_args['tol'] maxiter = ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = prod(rhoss.shape) L = L.data.tocsc() - (1e-15) * sp.eye(n, n, format='csc') L.sort_indices() orig_nnz = L.nnz # start with all ones as RHS v = np.ones(n, dtype=complex) if ss_args['use_rcm']: if settings.debug: old_band = sp_bandwidth(L)[0] logger.debug('Original bandwidth: %i' % old_band) perm = reverse_cuthill_mckee(L) rev_perm = np.argsort(perm) L = sp_permute(L, perm, perm, 'csc') v = v[np.ix_(perm, )] if settings.debug: new_band = sp_bandwidth(L)[0] logger.debug('RCM bandwidth: %i' % new_band) logger.debug('Bandwidth reduction factor: %f' % round(old_band / new_band, 2)) _power_start = time.time() # Get LU factors lu = splu(L, permc_spec=ss_args['permc_spec'], diag_pivot_thresh=ss_args['diag_pivot_thresh'], options=dict(ILU_MILU=ss_args['ILU_MILU'])) if settings.debug and _scipy_check: L_nnz = lu.L.nnz U_nnz = lu.U.nnz logger.debug('L NNZ: %i ; U NNZ: %i' % (L_nnz, U_nnz)) logger.debug('Fill factor: %f' % ((L_nnz + U_nnz) / orig_nnz)) it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = lu.solve(v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') _power_end = time.time() ss_args['info']['solution_time'] = _power_end - _power_start ss_args['info']['iterations'] = it if ss_args['return_info']: ss_args['info']['residual_norm'] = la.norm(L * v, np.inf) if settings.debug: logger.debug('Number of iterations: %i' % it) if ss_args['use_rcm']: v = v[np.ix_(rev_perm, )] # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if ss_args['return_info']: return rhoss, ss_args['info'] else: return rhoss
def partial_trace(rho_data, rho_dims, sel, perm=None): """Compute the partial trace of the matrix Parameters ---------- rho_data : csr_matrix or ndarray Matrix for which the partial trace will be computed rho_dims : list Subsystem dimensions sel : int/list An ``int`` or ``list`` of components to keep after partial trace. perm : ndarray Optionally a pre computed permutation matrix can be provided This may be more efficient if the same partial trace (sel) is to be computed for matices of the same sub-system dimensions. It can be calculated using the calc_perm function Returns ------- rho1_data : qobj Matrix representing partial trace with selected components remaining. rho1_dims : list Subsystem dimensions for output matrix rho1_shape : tuple Shape of output matrix """ #print("rho_dims {} (type: {})".format(rho_dims, type(rho_dims))) #print("sel {} (type: {})".format(sel, type(sel))) if isinstance(sel, int): sel = np.array([sel]) else: sel = np.asarray(sel) if (sel < 0).any() or (sel >= len(rho_dims[0])).any(): raise TypeError("Invalid selection index in ptrace.") sparse = sp.issparse(rho_data) if np.prod(rho_dims[1]) == 1: rho_data = rho_data.dot(rho_data.T.conj()) rho1_dims = [ np.asarray(rho_dims[0]).take(sel).tolist(), np.asarray(rho_dims[0]).take(sel).tolist() ] rho1_shape = (np.prod(rho1_dims[0]), np.prod(rho1_dims[1])) if perm is None: perm = calc_perm(rho_dims, sel, sparse=sparse) if sparse: rhdata = perm * sp_reshape(rho_data, [np.prod(rho_dims[0])**2, 1]) rhdata = rhdata.tolil().reshape(rho1_shape) rho1_data = rhdata.tocsr() else: rho1_data = np.reshape( np.sum(np.reshape(rho_data.flatten()[perm.flatten()], perm.shape), 1), rho1_shape) return rho1_data, rho1_dims, rho1_shape
def propagator(H, t, c_op_list=[], args={}, options=None, unitary_mode='batch', parallel=False, progress_bar=None, **kwargs): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. unitary_mode = str ('batch', 'single') Solve all basis vectors simulaneously ('batch') or individually ('single'). parallel : bool {False, True} Run the propagator in parallel mode. This will override the unitary_mode settings if set to True. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t td_type = _td_format_check(H, c_op_list, solver='me') if isinstance(H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims if parallel: unitary_mode = 'single' u = np.zeros([N, N, len(tlist)], dtype=complex) output = parallel_map(_parallel_sesolve,range(N), task_args=(N,H, tlist,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: if unitary_mode == 'single': u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() elif unitary_mode =='batch': u = np.zeros(len(tlist), dtype=object) _rows = np.array([(N+1)*m for m in range(N)]) _cols = np.zeros_like(_rows) _data = np.ones_like(_rows,dtype=complex) psi0 = Qobj(sp.coo_matrix((_data,(_rows,_cols))).tocsr()) if td_type[1] > 0 or td_type[2] > 0: H2 = [] for k in range(len(H)): if isinstance(H[k], list): H2.append([tensor(qeye(N), H[k][0]), H[k][1]]) else: H2.append(tensor(qeye(N), H[k])) else: H2 = tensor(qeye(N), H) output = sesolve(H2, psi0, tlist, [] , args = args, _safe_mode=False, options=Options(normalize_output=False)) for k, t in enumerate(tlist): u[k] = sp_reshape(output.states[k].data, (N, N)) unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0]) u[k] = u[k].T.tocsr() else: raise Exception('Invalid unitary mode.') elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(sqrt_N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(sqrt_N,sqrt_N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(sqrt_N,sqrt_N), dtype=complex)) output = mesolve(H, rho0, tlist, [], [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(N,N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(N,N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: if unitary_mode == 'batch': return Qobj(u[-1], dims=dims) else: return Qobj(u[:, :, 1], dims=dims) else: if unitary_mode == 'batch': return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))], dtype=object) else: return np.array([Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def propagator(H, t, c_op_list=[], args={}, options=None, unitary_mode='batch', parallel=False, progress_bar=None, _safe_mode=True, **kwargs): r""" Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\rho_{\mathrm vec}(t) = U(t) \rho_{\mathrm vec}(0)` where :math:`\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. unitary_mode = str ('batch', 'single') Solve all basis vectors simulaneously ('batch') or individually ('single'). parallel : bool {False, True} Run the propagator in parallel mode. This will override the unitary_mode settings if set to True. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if _safe_mode: _solver_safety_check(H, None, c_ops=c_op_list, e_ops=[], args=args) td_type = _td_format_check(H, c_op_list, solver='me') if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) if unitary_mode == 'batch': # batch don't work with function Hamiltonian unitary_mode = 'single' elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims if parallel: unitary_mode = 'single' u = np.zeros([N, N, len(tlist)], dtype=complex) output = parallel_map(_parallel_sesolve, range(N), task_args=(N, H, tlist, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: if unitary_mode == 'single': output = sesolve(H, qeye(dims[0]), tlist, [], args, options, _safe_mode=False) if len(tlist) == 2: return output.states[-1] else: return output.states elif unitary_mode == 'batch': u = np.zeros(len(tlist), dtype=object) _rows = np.array([(N + 1) * m for m in range(N)]) _cols = np.zeros_like(_rows) _data = np.ones_like(_rows, dtype=complex) psi0 = Qobj(sp.coo_matrix((_data, (_rows, _cols))).tocsr()) if td_type[1] > 0 or td_type[2] > 0: H2 = [] for k in range(len(H)): if isinstance(H[k], list): H2.append([tensor(qeye(N), H[k][0]), H[k][1]]) else: H2.append(tensor(qeye(N), H[k])) else: H2 = tensor(qeye(N), H) options.normalize_output = False output = sesolve(H2, psi0, tlist, [], args=args, options=options, _safe_mode=False) for k, t in enumerate(tlist): u[k] = sp_reshape(output.states[k].data, (N, N)) unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0]) u[k] = u[k].T.tocsr() else: raise Exception('Invalid unitary mode.') elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(sqrt_N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: rho0 = qeye(N, N) rho0.dims = [[sqrt_N, sqrt_N], [sqrt_N, sqrt_N]] output = mesolve(H, psi0, tlist, [], args, options, _safe_mode=False) if len(tlist) == 2: return output.states[-1] else: return output.states else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (N, N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(N, N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: if unitary_mode == 'batch': return Qobj(u[-1], dims=dims) else: return Qobj(u[:, :, 1], dims=dims) else: if unitary_mode == 'batch': return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))], dtype=object) else: return np.array( [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ ss_args['info'].pop('weight', None) if settings.debug: print('Starting iterative inverse-power method solver...') tol = ss_args['tol'] maxiter = ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = prod(rhoss.shape) L = L.data.tocsc() - (1e-15) * sp.eye(n, n, format='csc') L.sort_indices() orig_nnz = L.nnz # start with all ones as RHS v = np.ones(n,dtype=complex) if ss_args['use_rcm']: if settings.debug: old_band = sp_bandwidth(L)[0] print('Original bandwidth:', old_band) perm = reverse_cuthill_mckee(L) rev_perm = np.argsort(perm) L = sp_permute(L, perm, perm, 'csc') v = v[np.ix_(perm,)] if settings.debug: new_band = sp_bandwidth(L)[0] print('RCM bandwidth:', new_band) print('Bandwidth reduction factor:', round(old_band/new_band, 2)) # Get LU factors lu = splu(L, permc_spec=ss_args['permc_spec'], diag_pivot_thresh=ss_args['diag_pivot_thresh'], options=dict(ILU_MILU=ss_args['ILU_MILU'])) if settings.debug and _scipy_check: L_nnz = lu.L.nnz U_nnz = lu.U.nnz print('L NNZ:', L_nnz, ';', 'U NNZ:', U_nnz) print('Fill factor:', (L_nnz+U_nnz)/orig_nnz) _power_start = time.time() it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = lu.solve(v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') _power_end = time.time() ss_args['info']['solution_time'] = _power_end-_power_start ss_args['info']['iterations'] = it if settings.debug: print('Number of iterations:', it) if ss_args['use_rcm']: v = v[np.ix_(rev_perm,)] # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if ss_args['return_info']: return rhoss, ss_args['info'] else: return rhoss
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ ss_args['info'].pop('weight', None) if settings.debug: logger.debug('Starting iterative inverse-power method solver.') tol = ss_args['tol'] maxiter = ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = L.shape[0] # Build Liouvillian if settings.has_mkl and ss_args['method'] == 'power': has_mkl = 1 else: has_mkl = 0 L, perm, perm2, rev_perm, ss_args = _steadystate_power_liouvillian(L, ss_args, has_mkl) orig_nnz = L.nnz # start with all ones as RHS v = np.ones(n, dtype=complex) if ss_args['use_rcm']: v = v[np.ix_(perm2,)] # Do preconditioning if ss_args['M'] is None and ss_args['use_precond'] and \ ss_args['method'] in ['power-gmres', 'power-lgmres', 'power-bicgstab']: ss_args['M'], ss_args = _iterative_precondition(L, int(np.sqrt(n)), ss_args) if ss_args['M'] is None: warnings.warn("Preconditioning failed. Continuing without.", UserWarning) ss_iters = {'iter': 0} def _iter_count(r): ss_iters['iter'] += 1 return _power_start = time.time() # Get LU factors if ss_args['method'] == 'power': if settings.has_mkl: lu = mkl_splu(L) else: lu = splu(L, permc_spec=ss_args['permc_spec'], diag_pivot_thresh=ss_args['diag_pivot_thresh'], options=dict(ILU_MILU=ss_args['ILU_MILU'])) if settings.debug and _scipy_check: L_nnz = lu.L.nnz U_nnz = lu.U.nnz logger.debug('L NNZ: %i ; U NNZ: %i' % (L_nnz, U_nnz)) logger.debug('Fill factor: %f' % ((L_nnz+U_nnz)/orig_nnz)) it = 0 _tol = max(ss_args['tol']/10, 1e-15) # Should make this user accessible while (la.norm(L * v, np.inf) > tol) and (it < maxiter): if ss_args['method'] == 'power': v = lu.solve(v) elif ss_args['method'] == 'power-gmres': v, check = gmres(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], restart=ss_args['restart'], maxiter=ss_args['maxiter'], callback=_iter_count) elif ss_args['method'] == 'power-lgmres': v, check = lgmres(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], maxiter=ss_args['maxiter'], callback=_iter_count) elif ss_args['method'] == 'power-bicgstab': v, check = bicgstab(L, v, tol=_tol, M=ss_args['M'], x0=ss_args['x0'], maxiter=ss_args['maxiter'], callback=_iter_count) else: raise Exception("Invalid iterative solver method.") v = v / la.norm(v, np.inf) it += 1 if ss_args['method'] == 'power' and settings.has_mkl: lu.delete() if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') _power_end = time.time() ss_args['info']['solution_time'] = _power_end-_power_start ss_args['info']['iterations'] = it if ss_args['return_info']: ss_args['info']['residual_norm'] = la.norm(L*v) if settings.debug: logger.debug('Number of iterations: %i' % it) if ss_args['use_rcm']: v = v[np.ix_(rev_perm,)] # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if ss_args['return_info']: return rhoss, ss_args['info'] else: return rhoss