Source code for qutip.solver.integrator.qutip_integrator

from ..integrator import IntegratorException, Integrator
from ..solver_base import Solver
from .explicit_rk import Explicit_RungeKutta
import numpy as np
from qutip import data as _data


__all__ = ['IntegratorVern7', 'IntegratorVern9', 'IntegratorDiag']


[docs]class IntegratorVern7(Integrator): """ QuTiP's implementation of Verner's "most efficient" Runge-Kutta method of order 7. These are Runge-Kutta methods with variable steps and dense output. The implementation uses QuTiP's Data objects for the state, allowing sparse, GPU or other data layer objects to be used efficiently by the solver in their native formats. See https://www.sfu.ca/~jverner/ for a detailed description of the methods. Usable with ``method="vern7"`` """ integrator_options = { 'atol': 1e-8, 'rtol': 1e-6, 'nsteps': 1000, 'first_step': 0, 'max_step': 0, 'min_step': 0, 'interpolate': True, } support_time_dependant = True supports_blackbox = True method = 'vern7' def _prepare(self): self._ode_solver = Explicit_RungeKutta( self.system, method=self.method, **self.options ) self.name = self.method def get_state(self, copy=True): state = self._ode_solver.y return self._ode_solver.t, state.copy() if copy else state def set_state(self, t, state): self._ode_solver.set_initial_value(state.copy(), t) self._is_set = True def integrate(self, t, copy=True): self._ode_solver.integrate(t, step=False) self._check_failed_integration() return self.get_state(copy) def mcstep(self, t, copy=True): self._ode_solver.integrate(t, step=True) self._check_failed_integration() return self.get_state(copy) def _check_failed_integration(self): if self._ode_solver.successful(): return raise IntegratorException(self._ode_solver.status_message()) @property def options(self): """ Supported options by verner method: atol : float, default: 1e-8 Absolute tolerance. rtol : float, default: 1e-6 Relative tolerance. nsteps : int, default: 1000 Max. number of internal steps/call. first_step : float, default: 0 Size of initial step (0 = automatic). min_step : float, default: 0 Minimum step size (0 = automatic). max_step : float, default: 0 Maximum step size (0 = automatic) When using pulses, change to half the thinest pulse otherwise it may be skipped. interpolate : bool, default: True Whether to use interpolation step, faster most of the time. """ return self._options @options.setter def options(self, new_options): Integrator.options.fset(self, new_options)
[docs]class IntegratorVern9(IntegratorVern7): """ QuTiP's implementation of Verner's "most efficient" Runge-Kutta method of order 9. These are Runge-Kutta methods with variable steps and dense output. The implementation uses QuTiP's Data objects for the state, allowing sparse, GPU or other data layer objects to be used efficiently by the solver in their native formats. See https://www.sfu.ca/~jverner/ for a detailed description of the methods. Usable with ``method="vern9"`` """ integrator_options = { 'atol': 1e-8, 'rtol': 1e-6, 'nsteps': 1000, 'first_step': 0, 'max_step': 0, 'min_step': 0, 'interpolate': True, } method = 'vern9'
[docs]class IntegratorDiag(Integrator): """ Integrator solving the ODE by diagonalizing the system and solving analytically. It can only solve constant system and has a long preparation time, but the integration is fast. Usable with ``method="diag"`` """ integrator_options = {"eigensolver_dtype": "dense"} support_time_dependant = False supports_blackbox = False method = 'diag' def __init__(self, system, options): if not system.isconstant: raise ValueError("Hamiltonian system must be constant to use " "diagonalized method") super().__init__(system, options) def _prepare(self): self._dt = 0. self._expH = None H0 = self.system(0).to(self.options["eigensolver_dtype"]) self.diag, self.U = _data.eigs(H0.data, False) self.diag = self.diag.reshape((-1, 1)) self.Uinv = _data.inv(self.U) self.name = "qutip diagonalized" def integrate(self, t, copy=True): dt = t - self._t if dt == 0: return self.get_state() elif self._dt != dt: self._expH = np.exp(self.diag * dt) self._dt = dt self._y *= self._expH self._t = t return self.get_state(copy) def mcstep(self, t, copy=True): return self.integrate(t, copy=copy) def get_state(self, copy=True): return self._t, _data.matmul(self.U, _data.dense.Dense(self._y)) def set_state(self, t, state0): self._t = t self._y = _data.matmul(self.Uinv, state0).to_array() self._is_set = True @property def options(self): """ Supported options by "diag" method: eigensolver_dtype : str, default: "dense" Qutip data type {"dense", "csr", etc.} to use when computing the eigenstates. The dense eigen solver is usually faster and more stable. """ return self._options @options.setter def options(self, new_options): Integrator.options.fset(self, new_options)
Solver.add_integrator(IntegratorVern7, 'vern7') Solver.add_integrator(IntegratorVern9, 'vern9') Solver.add_integrator(IntegratorDiag, 'diag')