def _solve_ode(self, diff, y0, ode_inter, reformer, updater): OdeSolver = getattr(integrate, self.ode_method) in_real = self.ode_in_real t0 = self.time if t0 is None: t0 = 0.0 t1 = t0 + ode_inter if in_real: y0 = np.array((y0.real, y0.imag), dtype='float64') complex_diff = diff def diff(t, x): xc = np.array(x[0] + 1.0j * x[1], dtype='complex128') yc = complex_diff(t, xc) y = np.array((yc.real, yc.imag), dtype='float64') return y ode_solver = OdeSolver(diff, t0, y0, t1, vectorized=False) cmf_steps = self.cmf_steps for n in count(1): if ode_solver.status != 'running': logging.debug( __('* Propagation done. Average CMF steps: {}', n // cmf_steps)) break if n % cmf_steps == 0: if n >= self.max_ode_steps: msg = __('Reach ODE limit {}', n) logging.warning(msg) raise RuntimeWarning(msg) if reformer is not None: reformer() ode_solver.step() updater(ode_solver.y) return
def _sp_op(self, i, mat, h_list, mod_term, err=1.e-6): if not h_list: return np.zeros((mat.shape)) logging.debug(__('> OP on mat {}...', i)) n, m = mat.shape partial_transform = self._partial_transform a = self.get_sub_vec(-1) a_h = np.conj(a) density = self._partial_product(i, a, a_h) inv_density = linalg.inv(density + np.identity(m) * err) sp = self.get_sub_vec(i) sp_h = np.conj(np.transpose(sp)) projection = np.identity(n) - np.dot(sp, sp_h) tmp = partial_transform(i, a, mat) for mat_j in h_list: tmp = partial_transform(i, tmp, mat_j) for j, mat_j in mod_term: if j != i: tmp = partial_transform(j, tmp, mat_j) tmp = self._partial_product(i, tmp, a_h) ans = np.dot(projection, np.dot(tmp, inv_density)) return ans
def __init__(self, **kwargs): r""" Parameters ---------- e_list : [float] n := len(e_list) in the following context. omega_list : [float] m := len(omega_list) kappa_list : [[float]] (2D) n * m array such that kappa_list[i][j] = :math:`\kappa^{(i)}_j`. gamma_list : [[float]] (2D) n * m array such that kappa_list[i][j] = :math:`\gamma^{(i)}_j`. lambda_list : [[[float]]] (3D) n * n * m array such that kappa_list[i][j][k] = :math:`\lambda^{(ij)}_k`. """ valid_attributes = self.ELEC + self.VIB for name, value in kwargs.items(): if name in valid_attributes: setattr(self, name, value) else: logging.warning(__('Parameter {} unexpected, ignored.', name)) return
def _split_prop(self, tensor, tau=0.01, imaginary=False, cache=False): def diff(t, y): """This function will not change the arrays in tensor network. """ origin = tensor.array tensor.set_array(np.reshape(y, tensor.shape)) ans = np.zeros_like(y) self.time = t if not imaginary else None for n in self.term_visitor(use_cache=True): ans += np.reshape(self._single_eom(tensor, n, cache=cache), -1) if self.init_energy is not None: ans -= self.init_energy * y ans /= self.coefficient() tensor.set_array(origin) return ans def reformer(): self._form_env(update=True) return def updater(y): tensor.set_array(np.reshape(y, tensor.shape)) tensor.normalize(forced=(not imaginary)) if tensor.axis is None: self.root = tensor else: raise RuntimeError("Cannot propagate on Tensor {}" "which is not a root node!".format(tensor)) logging.debug(__("* Propagating at {} ({}) for {}", tensor, tensor.shape, tau)) y0 = np.reshape(tensor.array, -1) _re = None if self.f_list is None else reformer with self.log_inner_product(level=logging.DEBUG): self._solve_ode(diff, y0, tau, _re, updater) return tensor
def __init__(self, including_bath=False, relaxation_list=None, **kwargs): """ Needed parameters: - (for electronic part) 'e1', 'e2', 'v', - (for inner part) 'omega_list', 'lambda_list', 'dim_list', - (for outer part) 'stop', 'n', 'dim', 'lambda_g', 'omega_g', 'lambda_d', 'omega_d' """ # FIXME: including_bath=True case valid_attributes = self.ELEC + self.INNER + self.OUTER + self.FIELD for name, value in kwargs.items(): if name in valid_attributes: setattr(self, name, value) else: logging.warning(__('Parameter {} unexpected, ignored.', name)) self.elec_leaf = None self.inner_prefix = None self.outer_prefix = None self.leaves = [] self.dimensions = {} self.including_bath = including_bath h_list = self.electron_hamiltonian() all_vibriations = (self.inner_hamiltonian() + self.outer_hamiltonian() if including_bath else self.inner_hamiltonian()) h_list.extend(all_vibriations) if relaxation_list is not None: h_list.extend( self.relaxation_hamiltonian(coupling_list=relaxation_list, prefix='I')) self.h_list, self.f_list = self.collect_electric_terms(h_list) return
def _restart(self): logging.debug('Search space too large, restart.') logging.debug(__('ritz vals: {}', self._ritz_vals)) ritz_vals = self._ritz_vals self.__init__( matvec=self._matvec, init_vecs=self._get_ritz_vecs(), n_vals=self._n_vals ) self._ritz_vals = ritz_vals return
def log_inner_product(self, level=logging.DEBUG): root = self.root if logging.root.isEnabledFor(level): shape_dict = {} init = root.vectorize(shape_dict=shape_dict) try: yield self except: pass else: if logging.root.isEnabledFor(level): root.tensorize(np.conj(init), use_aux=True, shape_dict=shape_dict) ip = root.global_inner_product() logging.log(level, __("<|>:{}", ip))
def _normalizer(vec): ans = [] for i in range(self.rank + 1): vec_i = self.get_sub_vec(i, vec) vec_i = np.reshape(vec_i, -1) if i == self.rank: std_norm = sqrt(1.0) else: std_norm = sqrt(self.shape_list[i][1]) norm = (linalg.norm(vec_i) / std_norm) logging.debug(__('norm {}: {}', i, norm)) ans.append(vec_i / norm) ans = np.concatenate(ans, axis=None) return ans
def unlink_to(self, i, fast=False): """ Return ------ child : Tensor j : int """ try: if not fast: self.check_linkage(i) except KeyError: logging.info(__('No {0}-th linkage info of {1}', i, self)) else: child, j = self._access[i] self.unlink(self, i, child, j) return child, j
def check_completness(self, strict=False): """ Parameters ---------- strict : bool Whether to check the compatibility of arrays. """ if self.order is None: raise RuntimeError('No specific array at {0}'.format(self)) elif set(self._access) != set(range(self.order)): raise RuntimeError('Incomplete linkages info at {0}'.format(self)) else: for i in range(self.order): self.check_linkage(i, strict=strict) logging.info(__('Checked linkage completeness of {0}{1}', self, ' (strict)' if strict else '')) return
def config(cls, **kwargs): """Global configuration. Parameters ---------- tol : float, optional Tolerance of energy. max_cycle : int, optional max_space : int, optional lin_dep_lim : float, optional """ for key, value in kwargs.items(): try: setattr(cls, key, value) except AttributeError: logging.warning(__('No configuration "{}"!', key)) return
def link(a, i, b, j): r"""Add a linkage info between i-th order of a and j-th order of b. Parameters ---------- a : Tensor i : int b : Tensor j : int """ # Logging info for k, tensor in ((i, a), (j, b)): if k in tensor._access: logging.info(__('Overwrite the {0}-th linkage info of {1}', k, tensor)) a._access[i] = (b, j) b._access[j] = (a, i) return
def check_linkage(self, i, strict=False): """ Parameters ---------- i : int strict : bool Whether to check the compatibility of arrays. """ child, j = self._access[i] # raise KeyError if no such linkage condition = (child._access[j] == (self, i)) if strict: condition = condition and ( (isinstance(self, Leaf) and self._array is None) or (isinstance(child, Leaf) and child._array is None) or (not (self._array is None or child._array is None) and self.shape[i] == child.shape[j])) if condition: logging.debug(__('Checked {0}-th linkage info of {1}{2}', i, self, ' (strict)' if strict else '')) return else: raise RuntimeError('Wrong {0}-th linkage info of {1}'.format(i, self))
def term_hamiltonian(self, r, vec): """ Parameters ---------- r : int vec : (self.size,) ndarray """ logging.debug(__('Hamiltonian term {}...', r)) h_term = self.h_terms[r] mod_term = self.mod_terms[r] steps = len(self.shape_list) ans = [] for i in range(steps): v_i = self.get_sub_vec(i, vec) if i < steps - 1: h_list = [h for j, h in h_term if j == i] v_i = self._sp_op(i, v_i, h_list, mod_term) else: v_i = self._coeff_op(v_i, mod_term) v_i = np.reshape(v_i, -1) ans.append(v_i) ans = np.concatenate(ans, axis=None) return ans
def _is_converged(self): if ( self._last_ritz_vals is None or len(self._last_ritz_vals) != len(self._ritz_vals) ): self._convergence = [False] * len(self._ritz_vals) return False self._last_convergence = self._convergence diff_ritz_vals = np.abs(self._last_ritz_vals - self._ritz_vals) self._convergence = [ norm_ ** 2 < self.tol and diff_ritz_vals[i] < self.tol for i, norm_ in enumerate(self._residual_norms) ] if self._debug: for _i, _norm in enumerate(self._residual_norms): if self._convergence[_i] and not self._last_convergence[_i]: logging.debug( __('Root {} converged, norm = {:.8f}', _i, _norm) ) if all(self._convergence): return True else: return False
def propagation(self, init=None, start=0., stop=5., max_inter=0.01, const_energy=None, updater=None, normalizer=None): """A generator doing the propagation. Parameters ---------- init : (N,) ndarray, optional Real initial vector at t = t_0. start : float, optional Time starting point t_0. stop : float, optional End point t_1. max_inter : float, optional Maximum of time gap. const_enengy : bool, optional Whether to keep energy as a constant updater : -> a, optional Action after computing one step. normalizer : (N,) ndarray -> (N,) ndarray Yields ------ tuple : (float, ((N,) ndarray, (N,) ndarray)) (time, (real_vector, imaginary_vector)) """ if init is None: init = self.init_state() h_op = self.h_mat() e0 = self.energy_expection(init) length = len(init) init = init.astype(complex) factor = 1.0 / self.hbar def propagator(t, y): real, imag = y.real, y.imag real, imag = factor * h_op(imag), -factor * h_op(real) y = real + 1.0j * imag return y solver = RK45(propagator, start, init, stop, max_step=max_inter) while True: # Normalization if normalizer is not None: solver.y = normalizer(solver.y) t = solver.t y = solver.y real, imag = y.real, y.imag e = self.energy_expection(y) logging.info(__("t: {:.3f}, E: {:.8f}, |v|^2: {:.8f}", t, e, scipy.linalg.norm(y)**2)) if abs(e - e0) > 1.e-8: logging.warning('Energy is not conserved. ') if const_energy and abs(e - e0) > const_energy: logging.warning(__('Propagation stopped at t = {:.3f}.', solver.t)) raise StopIteration yield t, (real, imag) try: solver.step() except RuntimeError: logging.debug('Iterator normal terminated.') raise StopIteration else: if updater is not None: updater(solver)
def _coeff_op(self, tensor, mod_term): logging.debug(__('> OP on A tensor...')) for i, mat in mod_term: tensor = MCTDH._partial_transform(i, tensor, mat) return tensor