def _hamiltonian_rdot(M, other): new = other.copy() #deep=False: not implememnted in hamiltonian_core.copy() new._dtype = _np.result_type(M.dtype,new._dtype) new._static = _expm_multiply(M, other.static) new._dynamic = {func:_expm_multiply(M, Hd) for func,Hd in iteritems(other._dynamic)} return new
def _hamiltonian_dot(M, other): new = other.copy(deep=False) new._dtype = _np.result_type(M.dtype, new._dtype) new._static = _expm_multiply(M, other.static) new._dynamic = { func: _expm_multiply(M, Hd) for func, Hd in iteritems(other._dynamic) } return new
def _iter_rdot(M, other, step, grid): if grid[0] != 0: M *= grid[0] other = _expm_multiply(M, other) M /= grid[0] yield other.T.copy() M *= step for t in grid[1:]: other = _expm_multiply(M, other) yield other.T.copy()
def _expm_gen(psi, H, times, dt): """Generating function for evolution via `_expm_multiply`.""" if times[0] != 0: H *= times[0] psi = _expm_multiply(H, psi) H /= times[0] yield psi H *= dt for t in times[1:]: psi = _expm_multiply(H, psi) yield psi H /= dt
def _iter_sandwich(M, other, step, grid): if grid[0] != 0: M *= grid[0] other = _expm_multiply(M, other) other = _expm_multiply(M, other.T.conj()).T.conj() M /= grid[0] yield other.copy() M *= step for t in grid[1:]: other = _expm_multiply(M, other) other = _expm_multiply(M, other.T.conj()).T.conj() yield other.copy()
def rdot(self, other, shift=None, **call_kwargs): """Right-multiply an operator by matrix exponential. Let the matrix exponential object be :math:`\\exp(\\mathcal{O})` and let the operator be :math:`A`. Then this funcion implements: .. math:: A \\exp(\\mathcal{O}) Notes ----- For `hamiltonian` objects `A`, this function is the same as `A.dot(expO)`. Parameters ----------- other : obj The operator :math:`A` which multiplies from the left the matrix exponential :math:`\\exp(\\mathcal{O})`. shift : scalar Shifts operator to be exponentiated by a constant `shift` times the identity matrix: :math:`\\exp(\\mathcal{O} - \\mathrm{shift}\\times\\mathrm{Id})`. call_kwargs : obj, optional extra keyword arguments which include: **time** (*scalar*) - if the operator `O` to be exponentiated is a `hamiltonian` object. **pars** (*dict*) - if the operator `O` to be exponentiated is a `quantum_operator` object. Returns -------- obj matrix exponential multiplied by `other` from the left. Examples --------- >>> expO = exp_op(O) >>> A = exp_op(O,a=2j).get_mat() >>> print(expO.rdot(A)) >>> print(A.dot(expO)) """ is_sp = False is_ham = False if hamiltonian_core.ishamiltonian(other): shape = other._shape is_ham = True elif _sp.issparse(other): shape = other.shape is_sp = True elif other.__class__ in [_np.matrix, _np.ndarray]: shape = other.shape else: other = _np.asanyarray(other) shape = other.shape if other.ndim not in [1, 2]: raise ValueError( "Expecting a 1 or 2 dimensional array for 'other'") if other.ndim == 2: if shape[1] != self.get_shape[0]: raise ValueError( "Dimension mismatch between expO: {0} and other: {1}". format(self._O.get_shape, other.shape)) elif shape[0] != self.get_shape[0]: raise ValueError( "Dimension mismatch between expO: {0} and other: {1}".format( self._O.get_shape, other.shape)) if shift is not None: M = (self._a * (self.O(**call_kwargs) + shift * _sp.identity(self.Ns, dtype=self.O.dtype))).T else: M = (self._a * self.O(**call_kwargs)).T if self._iterate: if is_ham: return _hamiltonian_iter_rdot(M, other.T, self._step, self._grid) else: return _iter_rdot(M, other.T, self._step, self._grid) else: if self._grid is None and self._step is None: if is_ham: return _hamiltonian_rdot(M, other.T).T else: return _expm_multiply(M, other.T).T else: if is_sp: mats = _iter_rdot(M, other.T, self._step, self._grid) return _np.array([mat for mat in mats]) elif is_ham: mats = _hamiltonian_iter_rdot(M, other.T, self._step, self._grid) return _np.array([mat for mat in mats]) else: ver = [int(v) for v in scipy.__version__.split(".")] if _np.iscomplexobj(_np.float32(1.0).astype( M.dtype)) and ver[1] < 19: mats = _iter_rdot(M, other.T, self._step, self._grid) return _np.array([mat for mat in mats]) else: if other.ndim > 1: return _expm_multiply( M, other.T, start=self._start, stop=self._stop, num=self._num, endpoint=self._endpoint).transpose(0, 2, 1) else: return _expm_multiply(M, other.T, start=self._start, stop=self._stop, num=self._num, endpoint=self._endpoint)