def _lanczos_error_equation_to_optimize_delta_t(T, krylov_basis, t0, tf, target_tolerance): """ Function to optimize in order to obtain the optimal number of Lanczos algorithm iterations, governed by the optimal timestep size between Lanczos iteractions. """ eigenvalues1, eigenvectors1 = eigh(T[0:, 0:]) U1 = np.matmul(krylov_basis[0:, 0:].T, eigenvectors1) e01 = eigenvectors1.conj().T[:, 0] eigenvalues2, eigenvectors2 = eigh(T[0:-1, 0:T.shape[1] - 1]) U2 = np.matmul(krylov_basis[0:-1, :].T, eigenvectors2) e02 = eigenvectors2.conj().T[:, 0] def f(t): delta_t = -1j * (t - t0) aux1 = np.multiply(np.exp(delta_t * eigenvalues1), e01) psi1 = np.matmul(U1, aux1) aux2 = np.multiply(np.exp(delta_t * eigenvalues2), e02) psi2 = np.matmul(U2, aux2) error = np.linalg.norm(psi1 - psi2) steps = max(1, (tf - t0) // (t - t0)) return np.log10(error) + np.log10(steps) - np.log10(target_tolerance) return f
def _evolve(t0: float, krylov_basis: np.ndarray, T_m: np.ndarray): """ Computes the time evolution operator 'U(t - t0) psi0_k', where 'psi0_k' is the first basis element of the Krylov subspace, as a function of time. Parameters ------------ t0: float Initial time for the time evolution. krylov_basis: np.ndarray Krylov basis projector operator. T_m: np.ndarray Tridiagonal matrix decomposition of the system given by lanczos algorithm. Returns --------- time_evolution: function Time evolution given by the Krylov subspace approximation. """ eigenvalues, eigenvectors = eigh(T_m) U = np.matmul(krylov_basis.T, eigenvectors) e0 = eigenvectors.conj().T[:, 0] def time_evolution(t): delta_t = t - t0 aux = np.multiply(np.exp(-1j * delta_t * eigenvalues), e0) return np.matmul(U, aux) return time_evolution
def _wigner_fourier(psi, xvec, g=np.sqrt(2)): """ Evaluate the Wigner function via the Fourier transform. """ if psi.type == 'bra': psi = psi.dag() if psi.type == 'ket': return _psi_wigner_fft(psi.full(), xvec, g) elif psi.type == 'oper': eig_vals, eig_vecs = eigh(psi.full()) W = 0 for ii in range(psi.shape[0]): W1, yvec = _psi_wigner_fft( np.reshape(eig_vecs[:, ii], (psi.shape[0], 1)), xvec, g) W += eig_vals[ii] * W1 return W, yvec
def exactsolve(H, psi0, tlist): """Calculates exact solution by direct diagonalization.""" dims = psi0.dims H = H.full() psi0 = psi0.full() eigenvalues, eigenvectors = eigh(H) psi_base_diag = np.matmul(eigenvectors.conj().T, psi0) U = np.exp(np.outer(-1j * eigenvalues, tlist)) psi_list = np.matmul( eigenvectors, np.multiply(U, psi_base_diag.reshape([-1, 1])) ) exact_results = Result() exact_results.states = [Qobj(state, dims=dims) for state in psi_list.T] return exact_results
def __call__(self, state: Qobj): """ Get the Husimi-Q function for the given state vector or density matrix, over the coordinates used to initialise the class. If called multiple times, the states do not need to have the same dimensions, but none of them can have tensor-product structure. """ state = _qfunc_check_state(state) alphas = self._alphas(state.shape[0]) if state.isket: return self._single(state.full().ravel(), alphas) / np.pi # We don't use Qobj.eigenstates() to avoid building many unnecessary # CSR versions of dense matrices. values, vectors = eigh(state.full()) vectors = vectors.T out = values[0] * self._single(vectors[0], alphas) for value, vector in zip(values[1:], vectors[1:]): out += value * self._single(vector, alphas) return out / np.pi
def qfunc( state: Qobj, xvec, yvec, g: float = sqrt(2), precompute_memory: float = 1024, ): r""" Husimi-Q function of a given state vector or density matrix at phase-space points ``0.5 * g * (xvec + i*yvec)``. Parameters ---------- state : :obj:`.Qobj` A state vector or density matrix. This cannot have tensor-product structure. xvec, yvec : array_like x- and y-coordinates at which to calculate the Husimi-Q function. g : float, default sqrt(2) Scaling factor for ``a = 0.5 * g * (x + iy)``. The value of `g` is related to the value of :math:`\hbar` in the commutation relation :math:`[x,\,y] = i\hbar` via :math:`\hbar=2/g^2`, so the default corresponds to :math:`\hbar=1`. precompute_memory : real, default 1024 Size in MB that may be used during calculations as working space when dealing with density-matrix inputs. This is ignored for state-vector inputs. The bound is not quite exact due to other, order-of-magnitude smaller, intermediaries being necessary, but is a good approximation. If you want to use the same iterative algorithm for density matrices that is used for single kets, set ``precompute_memory=None``. Returns -------- ndarray Values representing the Husimi-Q function calculated over the specified range ``[xvec, yvec]``. See Also -------- :obj:`.QFunc` : a class-based version, more efficient if you want to calculate the Husimi-Q function for several states over the same coordinates. """ state = _qfunc_check_state(state) xvec, yvec = _qfunc_check_coordinates(xvec, yvec) required_memory = state.shape[0] * xvec.size * yvec.size * 16 / (1024**2) enough_memory = (precompute_memory is not None and precompute_memory > required_memory) if state.isoper and enough_memory: return QFunc(xvec, yvec, g)(state) if precompute_memory is not None and state.isoper: warnings.warn( "Falling back to iterative algorithm due to lack of memory." f" Needed {required_memory:.2f} MB, but only allowed to use" f" {precompute_memory:.2f} MB. Increase `precompute_memory` to" " raise limit, or set to `None` to suppress warning.") alpha_grid = _QFuncCoherentGrid(xvec, yvec, g) if state.isket: out = _qfunc_iterative_single(state.full().ravel(), alpha_grid, g) out /= np.pi return out # We don't use Qobj.eigenstates() to avoid building many unnecessary CSR # versions of dense matrices. values, vectors = eigh(state.full()) vectors = vectors.T out = values[0] * _qfunc_iterative_single(vectors[0], alpha_grid, g) for value, vector in zip(values[1:], vectors[1:]): out += value * _qfunc_iterative_single(vector, alpha_grid, g) out /= np.pi return out