├── VERSION.txt
├── examples
├── requirements.txt
├── example.py
├── pyheom_example_2level_cpu.ipynb
└── pyheom_example_2level_gpu.ipynb
├── .gitmodules
├── .gitignore
├── pyheom
├── redfield_solver.py
├── __init__.py
├── heom_solver.py
├── coo_matrix.py
├── const.py
├── unit.py
├── commuting_matrix.py
├── predefined_noise.py
├── pade_spectral_decomposition.py
├── qme_solver.py
├── noise_decomposition.py
└── summation_over_poles.py
├── src
├── CMakeLists.txt
└── pylibheom.cc.j2
├── INSTALL.md
├── CMakeLists.txt
├── LICENSE.txt
├── README.md
└── setup.py
/VERSION.txt:
--------------------------------------------------------------------------------
1 | 1.0.0a2
2 |
--------------------------------------------------------------------------------
/examples/requirements.txt:
--------------------------------------------------------------------------------
1 | tqdm
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "3rdparty/pybind11"]
2 | path = 3rdparty/pybind11
3 | url = https://github.com/pybind/pybind11.git
4 | [submodule "exts/libheom"]
5 | path = exts/libheom
6 | url = https://github.com/tatsushi-ikeda/libheom.git
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | *.exe
3 | *.exp
4 | *.lib
5 | *.dll
6 | *.a
7 | *.so
8 | *.obj
9 | *.dat
10 | *.bin
11 | *.pyc
12 | *.pyd
13 | *.aux
14 | *.log
15 | *.dvi
16 | *.pdf
17 | *.gif
18 | *.pdb
19 | *.swp
20 | build/*
21 | dist/*
22 | test/*
23 | *.egg-info/*
24 |
--------------------------------------------------------------------------------
/pyheom/redfield_solver.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | from .qme_solver import *
9 |
10 | class redfield_solver(qme_solver):
11 | qme_name = 'redfield'
12 |
13 | def storage_size(self):
14 | return 1
15 |
--------------------------------------------------------------------------------
/pyheom/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | from .predefined_noise import *
9 | from .noise_decomposition import *
10 | from .unit import *
11 | from .redfield_solver import *
12 | from .heom_solver import *
13 |
14 | def is_supported(engine):
15 | engine = engine.lower()
16 | func = getattr(libheom, f'{engine}_is_supported')
17 | if func:
18 | return func()
19 | else:
20 | raise Exception(f'Unknown linalg_engine: {engine}')
21 |
22 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # -*- mode:cmake -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | if (LIBHEOM_ENABLE_CUDA_INTERNAL)
9 | process_jinja2(pylibheom.cu SOURCE pylibheom.cc.j2)
10 | pybind11_add_module(pylibheom pylibheom.cu)
11 | set_target_properties(pylibheom PROPERTIES LINKER_LANGUAGE CUDA)
12 | set_target_properties(pylibheom PROPERTIES CUDA_ARCHITECTURES "${CUDA_ARCH_LIST}")
13 | else()
14 | process_jinja2(pylibheom.cc)
15 | pybind11_add_module(pylibheom pylibheom.cc)
16 | endif()
17 | target_link_libraries(pylibheom PRIVATE libheom)
18 |
--------------------------------------------------------------------------------
/pyheom/heom_solver.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | from .qme_solver import *
9 | from os import environ
10 | from multiprocessing import cpu_count
11 |
12 | class heom_solver(qme_solver):
13 | qme_name = 'heom'
14 |
15 | compulsory_args = [
16 | 'depth',
17 | ]
18 |
19 | optional_args = OrderedDict(
20 | n_inner_threads = 1,
21 | n_outer_threads = int(environ.get('OMP_NUM_THREADS', cpu_count())),
22 | )
23 |
24 | space_char = {
25 | 'hilbert': 'h',
26 | 'liouville': 'l',
27 | 'ado': 'a',
28 | }
29 |
30 | def storage_size(self):
31 | return self.qme_impl.get_n_hrchy() + 1
32 |
33 |
--------------------------------------------------------------------------------
/pyheom/coo_matrix.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import scipy as sp
10 | import scipy.sparse
11 | import pyheom.pylibheom as libheom
12 |
13 | from .const import *
14 |
15 | def libheom_coo_matrix(m):
16 | if isinstance(m, list):
17 | m = np.array(m)
18 | if m.dtype in DTYPE_CMPLX.keys():
19 | impl_class_name = f'coo_matrix_{DTYPE_CHAR[DTYPE_CMPLX[m.dtype]]}'
20 | else:
21 | raise TypeError(f'Unsupported matrix type: {m.dtype}')
22 | coo = sp.sparse.coo_matrix(m)
23 | dtype = DTYPE_CMPLX[m.dtype]
24 | return getattr(libheom, impl_class_name)(
25 | coo.shape[0],
26 | coo.shape[1],
27 | coo.nnz,
28 | coo.row,
29 | coo.col,
30 | coo.data.astype(dtype)
31 | )
32 |
--------------------------------------------------------------------------------
/pyheom/const.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 |
10 | DTYPE_CHAR = {
11 | np.dtype('complex64'): 'c',
12 | np.dtype('complex128'): 'z',
13 | }
14 |
15 | DTYPE_CMPLX = {
16 | np.dtype('float32'): np.dtype('complex64'),
17 | np.dtype('complex64'): np.dtype('complex64'),
18 | np.dtype('int64'): np.dtype('complex128'),
19 | np.dtype('float64'): np.dtype('complex128'),
20 | np.dtype('complex128'): np.dtype('complex128'),
21 | }
22 |
23 | FORMAT_CHAR = {
24 | 'dense': 'd',
25 | 'sparse': 's',
26 | }
27 |
28 | ORDER_CHAR = {
29 | 'row_major': 'r',
30 | 'col_major': 'c',
31 | True: 'r',
32 | False: 'c',
33 | }
34 |
35 | ORDER_CHAR_NUMPY = {
36 | 'row_major': 'C',
37 | 'col_major': 'F',
38 | True: 'C',
39 | False: 'F',
40 | }
41 |
42 | ENGINE_ARGS = {
43 | 'eigen': {},
44 | 'mkl': {},
45 | 'cuda': {'device':0},
46 | }
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/INSTALL.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Preparation
4 |
5 | First, you need to install the compulsory and optional dependent libraries.
6 | You can use the following command to fetch `libheom`, `Eigen3`, and `pybind11`.
7 |
8 | ```bash
9 | git submodule update --init --recursive
10 | ```
11 |
12 | If you want to enable `Intel MKL` and `CUDA` modules in `libheom`, you need to install and activate them before install `pyheom` (See `INSTALL.md` in `libheom`).
13 | Note that when you enable these modules, you need to make sure that `Intel MKL` and/or `CUDA` are activated at runtime.
14 | Otherwise, you will face import errors such as the following:
15 |
16 | ```text
17 | ImportError: libmkl_rt.so.1: cannot open shared object file: No such file or directory
18 | ```
19 |
20 | ## Installation
21 |
22 | Type the following:
23 |
24 | ```bash
25 | pip install .
26 | ```
27 |
28 | or
29 |
30 | ```bash
31 | pip install git+https://github.com/tatsushi-ikeda/pyheom
32 | ```
33 |
34 | You can specify arguments for cmake by using the environment variable `CMAKE_ARGS`.
35 | For example,
36 |
37 | ```bash
38 | CMAKE_ARGS="-DCMAKE_CXX_COMPILER=icc -DCUDA_ARCH_LIST=60 -DCMAKE_VERBOSE_MAKEFILE=ON" pip install -e . -v
39 | ```
40 |
41 | Verbose modes (`-v` for `pip` and `-DCMAKE_VERBOSE_MAKEFILE=ON` for `cmake`) will help you to specify details of installation.
42 |
43 | Regarding possible options in `CMAKE_ARGS`, see `INSTALL.md` in `libheom`
44 |
45 |
--------------------------------------------------------------------------------
/pyheom/unit.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import enum
9 | from sys import stderr, exit
10 |
11 | unit = enum.Enum('unit',
12 | '''dimensionless
13 | femtosecond
14 | picosecond
15 | wavenumber
16 | electronvolt''')
17 |
18 | hbar__J_s = 1.05457180013e-34
19 |
20 | UNIT_ENERGY_VALUE__J = {
21 | unit.wavenumber: 1.98644582441459e-23, # (299792458*100*6.62607004081e-34)
22 | unit.electronvolt: 1.602176620898e-19,
23 | };
24 | UNIT_TIME_VALUE__S = {
25 | unit.femtosecond: 1.0e-15,
26 | unit.picosecond: 1.0e-12,
27 | }
28 |
29 | units = {'energy':unit.dimensionless,
30 | 'time': unit.dimensionless}
31 |
32 | def calc_unit():
33 | if (units['energy'] == unit.dimensionless or units['time'] == unit.dimensionless):
34 | if (units['energy'] == unit.dimensionless and units['time'] == unit.dimensionless):
35 | result = 1.0
36 | else:
37 | print('[Error] Unit mismatch error: Both unit_energy and unit_time should be dimensionless.', file=stderr)
38 | exit(1)
39 | else:
40 | result = (UNIT_ENERGY_VALUE__J[units['energy']]
41 | *UNIT_TIME_VALUE__S[units['time']]
42 | /hbar__J_s)
43 | return result
44 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # -*- mode:cmake -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | cmake_minimum_required(VERSION 3.12)
9 |
10 | file(READ "VERSION.txt" VER)
11 | string(STRIP "${VER}" VER)
12 |
13 | project(pylibheom)
14 | set(PROJECT_VERSION ${VER})
15 |
16 | set(CMAKE_POSITION_INDEPENDENT_CODE ON)
17 |
18 | add_subdirectory(exts/libheom)
19 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/exts/libheom/cmake)
20 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_BINARY_DIR}/exts/libheom/cmake)
21 |
22 | include(libheom-common)
23 | include(libheom-default-compiler-setting)
24 |
25 | include_directories(${LIBHEOM_INCLUDES})
26 |
27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBHEOM_CXX_FLAGS}")
28 | set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${LIBHEOM_CUDA_FLAGS}")
29 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LIBHEOM_SHARED_LINKER_FLAGS}")
30 | set(CMAKE_SHARED_LIBRARY_CUDA_FLAGS "${CMAKE_SHARED_LIBRARY_CUDA_FLAGS} ${LIBHEOM_SHARED_LINKER_FLAGS}")
31 |
32 | find_package(PythonInterp 3.6 REQUIRED)
33 |
34 | if(EXISTS "${CMAKE_SOURCE_DIR}/3rdparty/pybind11")
35 | add_subdirectory(3rdparty/pybind11)
36 | message(STATUS "Found pybind11 Library in 3rdparty/ directory")
37 | else()
38 | find_package(pybind11 REQUIRED)
39 | endif()
40 |
41 | add_subdirectory(src)
42 |
43 |
44 |
--------------------------------------------------------------------------------
/pyheom/commuting_matrix.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import scipy as sp
10 |
11 | def get_commuting_matrix(c_vec, gamma, sigma):
12 | N = gamma.shape[0]
13 | if N == 1:
14 | # return np.array([[c_vec[0]]])
15 | return np.array([[c_vec[0]/sigma[0]]])
16 | basis = np.zeros((N,N), dtype=np.complex128)
17 | gammaT_n = np.identity(N, dtype=np.complex128) # gamma**n
18 | for n in range(N):
19 | basis[:,n] = basis[:,n] + (gammaT_n@sigma)
20 | gammaT_n = gammaT_n.dot(gamma.T)
21 | # basis_inv = np.linalg.inv(basis)
22 | basis_inv = np.linalg.solve(basis, np.identity(N, dtype=np.complex128))
23 | coeff = basis_inv@c_vec
24 |
25 | gamma_n = np.identity(N, dtype=np.complex128)
26 | c_mat = np.zeros((N,N), dtype=np.complex128)
27 | for n in range(N):
28 | c_mat += coeff[n]*gamma_n
29 | gamma_n = gamma_n@gamma
30 | return c_mat
31 |
32 | def get_commuting_matrix_diag(c_vec, gamma, sigma):
33 | N = gamma.shape[0]
34 | if N == 1:
35 | return np.array([[c_vec[0]]])
36 |
37 | s, U = sp.linalg.eig(gamma)
38 | Uinv = sp.linalg.inv(U)
39 | # print(Uinv@gamma@U)
40 | Delta = np.zeros((N,N), dtype=np.complex128)
41 | for j in range(N):
42 | Delta[j,j] = (c_vec@U[:,j])/(sigma@U[:,j])
43 | c_mat = U@Delta@Uinv
44 | return c_mat
45 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2019, tatsushi-ikeda
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/examples/example.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import scipy as sp
10 | import scipy.sparse
11 |
12 | from sys import stdout, stderr
13 | import time
14 |
15 | import pyheom
16 | from pyheom import heom_solver, redfield_solver, noise_decomposition, brown, unit
17 | pyheom.units['energy'] = unit.dimensionless
18 | pyheom.units['time'] = unit.dimensionless
19 | import tqdm
20 |
21 | dtype = np.complex128
22 | space = 'liouville'
23 | format = 'dense'
24 | engine = 'eigen'
25 | solver = 'lsrk4'
26 | order_liouville = 'row_major'
27 |
28 | lambda_0 = 0.01 # coupling constant (dimensionless)
29 | omega_0 = 1 # vibrational frequency (dimensionless)
30 | zeta = 0.5 # damping constant (dimensionless)
31 | T = 1 # temperature (dimensionless)
32 | depth = 5
33 |
34 | callback_interval = 2.5e-2
35 | count = 25
36 | t_list = np.arange(0, count, callback_interval)
37 | solver_params = dict(
38 | dt = 0.25e-2,
39 | # atol=1e-6, rtol=1e-3
40 | )
41 | #
42 |
43 | J = brown(lambda_0, zeta, omega_0)
44 |
45 | corr_dict = noise_decomposition(
46 | J,
47 | T = T,
48 | type_ltc = 'psd',
49 | n_psd = 1,
50 | type_psd = 'n-1/n'
51 | )
52 |
53 | n_level = 2
54 |
55 | omega_1 = np.sqrt(omega_0**2 - zeta**2*0.25)
56 | H = np.array([[omega_1, 0],
57 | [0, 0]],
58 | dtype=dtype)
59 |
60 | V = np.array([[0, 1],
61 | [1, 0]],
62 | dtype=dtype)
63 |
64 | qme = heom_solver(H, [dict(V=V, **corr_dict)],
65 | space=space, format=format, engine=engine,
66 | order_liouville=order_liouville,
67 | solver=solver,
68 | engine_args=dict(),
69 | depth = depth,
70 | n_inner_threads = 4,
71 | n_outer_threads = 1)
72 |
73 | n_storage = qme.storage_size()
74 |
75 |
76 | rho = np.zeros((n_storage,n_level,n_level), dtype=dtype)
77 | rho_0 = rho[0,:,:]
78 | rho_0[0,0] = 1
79 |
80 | with open('pop.dat', 'w') as out, \
81 | tqdm.tqdm(total=count) as bar:
82 | print('# density matrix dynamics', file=out)
83 | print('# time diabatic populations', file=out)
84 | def callback(t):
85 | bar.update(callback_interval)
86 | print(t, rho_0[0,0].real, rho_0[1,1].real, file=out)
87 | out.flush()
88 | begin = time.time()
89 | qme.solve(rho, t_list, callback=callback, **solver_params)
90 | end = time.time()
91 | print('elapsed:', end - begin, file=stderr)
92 | del qme
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # PyHEOM: Python 3 Library to Simulate Open Quantum Dynamics based on HEOM Theory
6 |
7 | The current stable version is [v0.5](https://github.com/tatsushi-ikeda/pyheom/tree/v0.5).
8 |
9 | Version [1.0 (alpha)](https://github.com/tatsushi-ikeda/pyheom/tree/master) is under development and could be unstable.
10 | In some environments, v0.5 may work more efficiently.
11 |
12 | ## Introduction
13 |
14 | `pyheom` is an open-source library that supports open quantum dynamics simulations based on the hierarchical equations of motion (HEOM) theory.
15 | This library provides a python 3 binding of `libheom` (`pylibheom`) and high-level APIs.
16 | All future development will be handled in this repository.
17 |
18 | This library is still under development, and some optional functions are not implemented.
19 | There are no guarantees about backward compatibility as of now (Version 0.6).
20 |
21 | ## TODO
22 |
23 | - Write API documentation
24 | - Rewrite codes for non-linear spectra calculations which are temporarily removed
25 |
26 | ## Required Packages
27 |
28 | - Python 3.6 or later
29 | - libheom and its dependent libraries:
30 | [https://github.com/tatsushi-ikeda/libheom](https://github.com/tatsushi-ikeda/libheom)
31 | - pybind11:
32 | [https://github.com/pybind/pybind11](https://github.com/pybind/pybind11)
33 | - numpy:
34 | [https://numpy.org/](https://numpy.org/)
35 | - scipy:
36 | [https://www.scipy.org/](https://www.scipy.org/)
37 | - jinja2:
38 | [https://jinja.palletsprojects.com](https://jinja.palletsprojects.com/en/3.1.x/)
39 |
40 | ## Installation
41 |
42 | Type the following command from the source tree directory:
43 |
44 | ```bash
45 | pip install .
46 | ```
47 |
48 | or
49 |
50 | ```
51 | pip install git+https://github.com/tatsushi-ikeda/pyheom
52 | ```
53 |
54 | You can specify arguments for cmake by using the environment variable `CMAKE_ARGS`.
55 | For details, see [INSTALL.md](INSTALL.md).
56 |
57 | This [Google Colaboratory example](https://colab.research.google.com/github/tatsushi-ikeda/pyheom/blob/develop/examples/pyheom_example_2level_cpu.ipynb) ([GPGPU version](https://colab.research.google.com/github/tatsushi-ikeda/pyheom/blob/develop/examples/pyheom_example_2level_gpu.ipynb)) may be helpful.
58 |
59 |
60 |
61 |
62 |
63 | ## Authors
64 | * **Tatsushi Ikeda** (ikeda.tatsushi.37u@kyoto-u.jp)
65 |
66 | ## Licence
67 | [](http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22Revised_BSD_License.22.2C_.22New_BSD_License.22.2C_or_.22Modified_BSD_License.22.29)
68 |
69 | `libheom` and `pyheom` are distributed under the BSD 3-clause License. See the `LICENSE.txt` file for details.
70 |
71 | ## Citation Information
72 |
73 | ```Plain Text
74 | @article{ikeda2020jcp,
75 | author = {Ikeda, Tatsushi and Scholes, Gregory D.},
76 | title = {Generalization of the hierarchical equations of motion theory for efficient calculations with arbitrary correlation functions},
77 | journal = {The Journal of Chemical Physics},
78 | volume = {152},
79 | number = {20},
80 | pages = {204101},
81 | ISSN = {0021-9606},
82 | DOI = {10.1063/5.0007327},
83 | url = {https://doi.org/10.1063/5.0007327},
84 | year = {2020},
85 | type = {Journal Article}
86 | }
87 | ```
88 |
89 | ## Acknowledgments
90 |
91 |
92 |
93 |
94 |
95 |
96 | - A prototype of this library was developed for projects supported by [Japan Society for the Promotion of Science](https://www.jsps.go.jp/).
97 | The current version is being developed for projects funded by JSPS again.
98 | - The version for the above research paper (v0.5) was developed in [the Scholes group](http://chemlabs.princeton.edu/scholes/) for projects supported by [the Gordon and Betty Moore Foundation](https://www.moore.org/).
99 |
--------------------------------------------------------------------------------
/pyheom/predefined_noise.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import cmath as cm
10 |
11 | class drude:
12 | def __init__(self, eta, gamma_c):
13 | self.eta = float(eta)
14 | self.gamma_c = float(gamma_c)
15 | self.name = 'drude'
16 | self.poles = self.get_poles()
17 |
18 | def get_poles(self):
19 | eta = self.eta
20 | gamma_c = self.gamma_c
21 | return [[gamma_c, eta*gamma_c**2, 1, 0]]
22 |
23 | def spectrum(self, omega):
24 | eta = self.eta
25 | gamma_c = self.gamma_c
26 | return eta*gamma_c**2*omega/(omega**2+gamma_c**2)
27 |
28 |
29 | class brown:
30 | def __init__(self, lambda_0, gamma_c, omega_0):
31 | self.lambda_0 = float(lambda_0)
32 | self.gamma_c = float(gamma_c)
33 | self.omega_0 = float(omega_0)
34 | self.name = 'brown'
35 | self.poles = self.get_poles()
36 |
37 | def get_poles(self):
38 | lambda_0 = self.lambda_0
39 | gamma_c = self.gamma_c
40 | omega_0 = self.omega_0
41 |
42 | omega_1 = cm.sqrt(omega_0**2 - gamma_c**2*0.25)
43 |
44 | if np.abs(omega_1) < (np.finfo(float).eps):
45 | # critical damped
46 | coef = 2*lambda_0*gamma_c*omega_0**2
47 | return [[gamma_c*0.5, coef, 2, 0]]
48 |
49 | gamma_p = gamma_c*0.5 + 1.0j*omega_1
50 | coef_p = +1.0j*lambda_0*omega_0**2/omega_1
51 | gamma_m = gamma_c*0.5 - 1.0j*omega_1
52 | coef_m = -1.0j*lambda_0*omega_0**2/omega_1
53 |
54 | if gamma_p.imag == 0.0:
55 | # overdamped
56 | return [[gamma_p.real, coef_p.real, 1, 0],
57 | [gamma_m.real, coef_m.real, 1, 0]]
58 | else:
59 | # underdamped
60 | return [[gamma_p, coef_p, 1, 0],
61 | [gamma_m, coef_m, 1, 0]]
62 |
63 | def spectrum(self, omega):
64 | lambda_0 = self.lambda_0
65 | gamma_c = self.gamma_c
66 | omega_0 = self.omega_0
67 |
68 | return 2*lambda_0*gamma_c*omega_0**2*omega \
69 | /((omega_0**2 - omega**2)**2 + gamma_c**2*omega**2)
70 |
71 |
72 | class overdamped_brown(drude):
73 | def __init__(self, lambda_0, gamma_c, omega_0):
74 | self.gamma_c = float(omega_0**2/gamma_c)
75 | self.eta = float(2*lambda_0/self.gamma_c)
76 | self.name = 'overdamped_brown'
77 | self.poles = self.get_poles()
78 |
79 |
80 | class brown_drude:
81 | def __init__(self, lambda_0, zeta, gamma_c, omega_0):
82 | self.lambda_0 = float(lambda_0)
83 | self.zeta = float(zeta)
84 | self.gamma_c = float(gamma_c)
85 | self.omega_0 = float(omega_0)
86 |
87 | def get_name(self):
88 | return 'brown_drude'
89 |
90 | def calc_r_k(self):
91 | lambda_0 = self.lambda_0
92 | zeta = self.zeta
93 | gamma_c = self.gamma_c
94 | omega_0 = self.omega_0
95 |
96 | c3 = 1.0
97 | c2 = -2*omega_0**2 + gamma_c**2 - 2*zeta*gamma_c
98 | c1 = omega_0**4 + (-2*gamma_c**2 + 2*zeta*gamma_c)*omega_0**2 + zeta**2*gamma_c**2
99 | c0 = gamma_c**2*omega_0**4
100 |
101 | omega2_k = np.roots([c3, c2, c1, c0])
102 | gamma_k = np.array([cm.sqrt(omega2)/1.0j if cm.sqrt(omega2).imag >= 0.0 else -cm.sqrt(omega2)/1.0j for omega2 in omega2_k])
103 | # if np.abs(omega_1) < (np.finfo(float).eps):
104 | # raise Exception('Calculation for critical damping case is unsupported feature.')
105 |
106 | Gamma = (gamma_k[0]**2-gamma_k[1]**2)*(gamma_k[1]**2-gamma_k[2]**2)*(gamma_k[2]**2-gamma_k[0]**2)
107 |
108 | coef_t = -2*lambda_0*omega_0**2*zeta*gamma_c**2/Gamma
109 | coef_0 = coef_t*(gamma_k[1]**2 - gamma_k[2]**2)
110 | coef_1 = coef_t*(gamma_k[2]**2 - gamma_k[0]**2)
111 | coef_2 = coef_t*(gamma_k[0]**2 - gamma_k[1]**2)
112 |
113 | r_k = np.array([coef_0, coef_1, coef_2])
114 | return [[gamma_k[0], coef_0, 1, 0],
115 | [gamma_k[1], coef_1, 1, 0],
116 | [gamma_k[2], coef_2, 1, 0]]
117 |
118 | def spectrum(self, omega):
119 | lambda_0 = self.lambda_0
120 | zeta = self.zeta
121 | gamma_c = self.gamma_c
122 | omega_0 = self.omega_0
123 |
124 | deltaOmega2 = zeta*omega**2*gamma_c/(omega**2 + gamma_c**2)
125 | deltaGamma2 = zeta*omega*gamma_c**2/(omega**2 + gamma_c**2)
126 |
127 | return 2.0*lambda_0*omega_0**2*deltaGamma2 \
128 | /((self.omega_0**2 - omega**2 + deltaOmega2)**2 + deltaGamma2**2)
129 |
--------------------------------------------------------------------------------
/pyheom/pade_spectral_decomposition.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import cmath as cm
10 |
11 | def psd(n, type_pade):
12 | type_pade = type_pade.lower()
13 | if (type_pade == 'n/n'):
14 | return psd_n(n)
15 | elif (type_pade == 'n-1/n'):
16 | return psd_nm1(n)
17 | elif (type_pade == 'n+1/n'):
18 | return psd_np1(n)
19 | else:
20 | raise Exception('[Error] Undefined type_pade: {}'.format(type_pade))
21 |
22 | def psd_n(n, dtype=np.float64):
23 | if n == 0:
24 | return np.zeros(0), np.zeros(0), 1/12.0, 0.0
25 |
26 | m = 2*n + 1
27 | b = np.array([2*i + 3 for i in range(m)], dtype=np.float64)
28 |
29 | Lambda = np.zeros((m, m), dtype=np.float64)
30 | for i in range(m - 1):
31 | Lambda[i,i+1] = 1.0/np.sqrt(b[i]*b[i+1])
32 | Lambda[i+1,i] = Lambda[i,i+1]
33 |
34 | lambda_eig, _ = np.linalg.eigh(Lambda)
35 | lambda_eig.sort()
36 | xi = np.array([-2.0/l for l in lambda_eig[0:n]])
37 |
38 | Lambda_ = np.zeros((m-1, m-1), dtype=np.float64)
39 | for i in range(m - 2):
40 | Lambda_[i,i+1] = 1.0/np.sqrt(b[i+1]*b[i+2])
41 | Lambda_[i+1,i] = Lambda_[i,i+1]
42 | lambda_eig_, _ = np.linalg.eigh(Lambda_)
43 | lambda_eig_.sort()
44 |
45 | zeta = np.array([-2.0/l for l in lambda_eig_[0:n]])
46 |
47 | R = 1/(4*(n + 1)*b[n])
48 |
49 | eta = np.empty(n)
50 | for i in range(n):
51 | eta[i] = R/2.0;
52 | for k in range(n):
53 | eta[i] *= zeta[k]**2 - xi[i]**2
54 | if k != i:
55 | eta[i] /= xi[k]**2 - xi[i]**2
56 |
57 | T = 0
58 |
59 | return xi, eta, R, T
60 |
61 | def psd_nm1(n):
62 | if (n == 0):
63 | raise
64 |
65 | m = 2*n
66 | b = np.array([2*i + 3 for i in range(m)], dtype=np.float64)
67 |
68 | Lambda = np.zeros((m, m), dtype=np.float64)
69 | for i in range(m - 1):
70 | Lambda[i,i+1] = 1.0/np.sqrt(b[i]*b[i+1])
71 | Lambda[i+1,i] = Lambda[i,i+1]
72 |
73 | lambda_eig, _ = np.linalg.eigh(Lambda)
74 | lambda_eig.sort()
75 | xi_ = np.array([-2.0/l for l in lambda_eig[0:n]])
76 |
77 | Lambda_ = np.zeros((m-1, m-1), dtype=np.float64)
78 | for i in range(m - 2):
79 | Lambda_[i,i+1] = 1.0/np.sqrt(b[i+1]*b[i+2])
80 | Lambda_[i+1,i] = Lambda_[i,i+1]
81 | lambda_eig_, _ = np.linalg.eigh(Lambda_)
82 | lambda_eig_.sort()
83 |
84 | with np.errstate(divide='ignore', invalid='ignore'):
85 | zeta_ = np.array([-2.0/l for l in lambda_eig_[0:n]])
86 |
87 | R_ = 0.0
88 |
89 | eta_ = np.empty(n)
90 | for i in range(n):
91 | eta_[i] = n*b[n]/2.0;
92 | for k in range(n):
93 | if k != n - 1:
94 | eta_[i] *= zeta_[k]**2 - xi_[i]**2
95 | if k != i:
96 | eta_[i] /= xi_[k]**2 - xi_[i]**2
97 |
98 | T_ = 0.0
99 |
100 | return xi_, eta_, R_, T_
101 |
102 |
103 | def psd_np1(N):
104 | if (N == 0):
105 | raise
106 |
107 | M = 2*N + 2
108 |
109 | # calc b[m]
110 | b = np.array([2*m + 3 for m in range(M)], dtype=np.float64)
111 |
112 | # calc d[m]
113 | d = np.zeros((M), dtype=np.float64)
114 | d[0] = 1/(4*b[0])
115 | for m in range(1,M//2+1):
116 | d[2*m-1] = -4*m**2*b[m-1]**2*b[2*m-1]
117 | for m in range(1,M//2):
118 | d[2*m] = -b[2*m]/(4*m*(m+1)*b[m-1]*b[m])
119 |
120 | # calc T_caron[k]
121 | T_caron = np.zeros((N+1), dtype=np.float64)
122 | for k in range(0,N+1):
123 | denom = 0.0
124 | for n in range(1,k+2):
125 | denom += d[2*n-1]
126 | T_caron[k]=1/(4*denom)
127 | T_caron_N = T_caron[N]
128 |
129 | # calc R_caron[N]
130 | summation = 0.0
131 | for m in range(1,N+2):
132 | inner_summation = 0.0
133 | for k in range(m, N+2):
134 | inner_summation += d[2*k-1]
135 | summation += d[2*m-2]*inner_summation**2
136 | R_caron_N = (4*T_caron[N])**2*summation
137 |
138 | # calc xi_caron
139 | Lambda_prime = np.zeros((M, M), dtype=np.float64)
140 | for n in range(1,M):
141 | m = n+1
142 | if (m <= M-1):
143 | Lambda_prime[m,n] = 1.0/np.sqrt(d[m]*d[n])
144 | m = n-1
145 | if (m >= 1):
146 | Lambda_prime[m,n] = 1.0/np.sqrt(d[m]*d[n])
147 | lambda_prime_eig, _ = np.linalg.eigh(Lambda_prime[1:M,1:M])
148 | lambda_prime_eig.sort()
149 | xi_caron = np.zeros((N+1), dtype=np.float64)
150 | xi_caron[1:N+1] = np.array([-2.0/l for l in lambda_prime_eig[0:N]])
151 |
152 |
153 | # calc t[k]
154 | t = np.zeros((N+2), dtype=np.float64)
155 | t[1] = T_caron[0]
156 | for k in range(1,N+1):
157 | t[k+1] = T_caron[k]/T_caron[k-1]
158 |
159 | # calc eta_caron
160 | eta_caron = np.zeros((N+1), dtype=np.complex64)
161 | for j in range(1,N+1):
162 | delta = np.zeros((N+2), dtype=np.complex128)
163 | for k in range(1,N+1):
164 | delta[k] = xi_caron[k]**2 - xi_caron[j]**2
165 | delta[j] = 1.0
166 | delta[N+1] = 1.0
167 | r = np.zeros((2*N+3), dtype=np.complex128)
168 | for k in range(1,N+2):
169 | r[2*k-1] = cm.sqrt(4*np.abs(t[k]/delta[k]))
170 | r[2*k] = r[2*k-1]*np.sign(t[k]/delta[k])
171 | X = np.zeros((2*N+3), dtype=np.complex128)
172 | X[0] = 0.5
173 | X[1] = d[0]*r[1]*X[0]
174 | for m in range(2,2*N+3):
175 | X[m] = d[m-1]*r[m]*X[m-1] - r[m]*r[m-1]*xi_caron[j]**2*X[m-2]/4
176 | eta_caron[j] = X[2*N+2]
177 |
178 | return xi_caron[1:], eta_caron[1:], R_caron_N, T_caron_N
179 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # PyHEOM: Copyright (c) Tatsushi Ikeda
3 | # This library is distributed under BSD 3-Clause License.
4 | # See LINCENSE.txt for licence.
5 | # ------------------------------------------------------------------------
6 |
7 | import os
8 | import re
9 | import subprocess
10 | import sys
11 |
12 | # The original source of the cmake build script below is https://github.com/pybind/cmake_example
13 |
14 | from setuptools import setup, Extension
15 | from setuptools.command.build_ext import build_ext
16 |
17 | # Convert distutils Windows platform specifiers to CMake -A arguments
18 | PLAT_TO_CMAKE = {
19 | "win32": "Win32",
20 | "win-amd64": "x64",
21 | "win-arm32": "ARM",
22 | "win-arm64": "ARM64",
23 | }
24 |
25 | # A CMakeExtension needs a sourcedir instead of a file list.
26 | # The name must be the _single_ output extension from the CMake build.
27 | # If you need multiple extensions, see scikit-build.
28 | class CMakeExtension(Extension):
29 | def __init__(self, name, sourcedir=""):
30 | Extension.__init__(self, name, sources=[])
31 | self.sourcedir = os.path.abspath(sourcedir)
32 |
33 |
34 | class CMakeBuild(build_ext):
35 | def build_extension(self, ext):
36 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
37 |
38 | # required for auto-detection & inclusion of auxiliary "native" libs
39 | if not extdir.endswith(os.path.sep):
40 | extdir += os.path.sep
41 |
42 | debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug
43 | cfg = "Debug" if debug else "Release"
44 |
45 | # CMake lets you override the generator - we need to check this.
46 | # Can be set with Conda-Build, for example.
47 | cmake_generator = os.environ.get("CMAKE_GENERATOR", "")
48 |
49 | # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON
50 | # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code
51 | # from Python.
52 | cmake_args = [
53 | "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}".format(extdir),
54 | "-DPYTHON_EXECUTABLE={}".format(sys.executable),
55 | "-DCMAKE_BUILD_TYPE={}".format(cfg), # not used on MSVC, but no harm
56 | ]
57 | build_args = []
58 | # Adding CMake arguments set as environment variable
59 | # (needed e.g. to build for ARM OSx on conda-forge)
60 | if "CMAKE_ARGS" in os.environ:
61 | cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item]
62 |
63 | # In this example, we pass in the version to C++. You might not need to.
64 | # cmake_args += [
65 | # "-DEXAMPLE_VERSION_INFO={}".format(self.distribution.get_version())
66 | # ]
67 |
68 | if self.compiler.compiler_type != "msvc":
69 | # Using Ninja-build since it a) is available as a wheel and b)
70 | # multithreads automatically. MSVC would require all variables be
71 | # exported for Ninja to pick it up, which is a little tricky to do.
72 | # Users can override the generator with CMAKE_GENERATOR in CMake
73 | # 3.15+.
74 | if not cmake_generator:
75 | try:
76 | import ninja # noqa: F401
77 |
78 | cmake_args += ["-GNinja"]
79 | except ImportError:
80 | pass
81 |
82 | else:
83 |
84 | # Single config generators are handled "normally"
85 | single_config = any(x in cmake_generator for x in {"NMake", "Ninja"})
86 |
87 | # CMake allows an arch-in-generator style for backward compatibility
88 | contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"})
89 |
90 | # Specify the arch if using MSVC generator, but only if it doesn't
91 | # contain a backward-compatibility arch spec already in the
92 | # generator name.
93 | if not single_config and not contains_arch:
94 | cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]]
95 |
96 | # Multi-config generators have a different way to specify configs
97 | if not single_config:
98 | cmake_args += [
99 | "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir)
100 | ]
101 | build_args += ["--config", cfg]
102 |
103 | if sys.platform.startswith("darwin"):
104 | # Cross-compile support for macOS - respect ARCHFLAGS if set
105 | archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", ""))
106 | if archs:
107 | cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))]
108 |
109 | # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level
110 | # across all generators.
111 | if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ:
112 | # self.parallel is a Python 3 only way to set parallel jobs by hand
113 | # using -j in the build_ext call, not supported by pip or PyPA-build.
114 | if hasattr(self, "parallel") and self.parallel:
115 | # CMake 3.12+ only.
116 | build_args += ["-j{}".format(self.parallel)]
117 |
118 | if not os.path.exists(self.build_temp):
119 | os.makedirs(self.build_temp)
120 |
121 | subprocess.check_call(
122 | ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp
123 | )
124 | subprocess.check_call(
125 | ["cmake", "--build", "."] + build_args, cwd=self.build_temp
126 | )
127 |
128 | with open('VERSION.txt', 'r') as inp:
129 | version = inp.read().strip()
130 |
131 | setup(
132 | name='pyheom',
133 | version=version,
134 | author='Tatsushi IKEDA',
135 | author_email='ikeda.tatsushi.37u@kyoto-u.jp',
136 | packages=['pyheom'],
137 | ext_modules=[CMakeExtension("pyheom.pylibheom")],
138 | cmdclass={"build_ext": CMakeBuild},
139 | install_requires=['numpy', 'scipy', 'jinja2'],
140 | zip_safe=False
141 | )
142 |
143 | # from setuptools import setup
144 | # import sys
145 | # import os
146 |
147 | # setup(name='pyheom',
148 | # version='0.6.20',
149 | # author='Tatsushi IKEDA',
150 | # author_email='ikeda.tatsushi.37u@kyoto-u.jp',
151 | # install_requires=['pylibheom>=0.6.18', 'numpy', 'scipy', 'jinja2'],
152 | # packages=['pyheom'],
153 | # package_dir={'pyheom':'pyheom'},
154 | # zip_safe=False)
155 |
156 |
--------------------------------------------------------------------------------
/pyheom/qme_solver.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import pyheom.pylibheom as libheom
9 |
10 | import numpy as np
11 | import pyheom.pylibheom as libheom
12 | from collections import OrderedDict
13 | from abc import ABCMeta, abstractmethod
14 |
15 | from .unit import *
16 | from .const import *
17 | from .coo_matrix import *
18 |
19 | class qme_solver:
20 | __metaclass__ = ABCMeta
21 |
22 | space_char = {
23 | 'hilbert': 'h',
24 | 'liouville': 'l',
25 | }
26 |
27 | qme_name = 'qme'
28 |
29 | compulsory_args = []
30 | optional_args = OrderedDict()
31 |
32 | def get_config(self,
33 | qme_name,
34 | engine,
35 | solver,
36 | dtype,
37 | space,
38 | format,
39 | c_contiguous,
40 | n_level,
41 | unrolling,
42 | c_contiguous_liouville):
43 | c_dtype = DTYPE_CHAR[dtype]
44 | c_format = FORMAT_CHAR[format.lower()]
45 | c_order = ORDER_CHAR[c_contiguous]
46 | c_space = self.space_char[space.lower()]
47 | if unrolling and engine.lower() == 'eigen' and n_level in [2,]:
48 | c_level = f'{n_level}'
49 | else:
50 | c_level = 'n'
51 | if c_space in ['l', 'a']:
52 | c_order_liouville = ORDER_CHAR[c_contiguous_liouville]
53 | else:
54 | c_order_liouville = ''
55 | return dict(qme_name=qme_name, engine=engine, solver=solver, c_dtype=c_dtype, c_space=c_space, c_format=c_format, c_order=c_order, c_level=c_level, c_order_liouville=c_order_liouville)
56 |
57 | @staticmethod
58 | def get_class(name_format, config):
59 | class_name = name_format.format(**config)
60 | class_obj = getattr(libheom, class_name)
61 | if class_obj:
62 | return class_obj
63 | else:
64 | raise Exception(f'[Error:] unknown class: {class_name}')
65 |
66 | def __init__(self,
67 | H,
68 | noises,
69 | space='hilbert',
70 | format='dense',
71 | engine='eigen',
72 | unrolling=True,
73 | solver='lsrk4',
74 | order_liouville='row_major',
75 | engine_args={},
76 | **args):
77 | engine = engine.lower()
78 |
79 | self.H = H
80 | self.noises = noises
81 | self.dtype = H.dtype
82 | self.n_level = H.shape[0]
83 | self.c_contiguous = H.flags.c_contiguous
84 | self.config = self.get_config(self.qme_name,
85 | engine,
86 | solver,
87 | self.dtype,
88 | space,
89 | format,
90 | self.c_contiguous,
91 | self.n_level,
92 | unrolling,
93 | order_liouville)
94 |
95 | self.engine_impl = qme_solver.get_class('{engine}', self.config)(
96 | *(dict(ENGINE_ARGS[engine], **engine_args).values())
97 | )
98 |
99 | given_keys = set(args.keys())
100 | compulsory_keys = set(self.compulsory_args)
101 | optional_keys = set(self.optional_args.keys())
102 | if not compulsory_keys.issubset(given_keys):
103 | missing_keys = compulsory_keys - given_keys
104 | raise KeyError(f'Missing keyword arguments: {", ".join(missing_keys)}')
105 | given_keys -= compulsory_keys
106 | if not given_keys.issubset(optional_keys):
107 | unknown_keys = given_keys - optional_keys
108 | raise KeyError(f'Unknown keyword arguments: {", ".join(unknown_keys)}')
109 | self.qme_args = OrderedDict((key,None) for key in self.compulsory_args)
110 | self.qme_args.update(self.optional_args)
111 | self.qme_args.update(args)
112 |
113 | self.qme_impl = qme_solver.get_class(
114 | '{qme_name}_{c_dtype}{c_space}{c_format}{c_order}{c_order_liouville}{c_level}_{engine}',
115 | self.config)(
116 | *(self.qme_args.values())
117 | )
118 | self.qme_impl.set_system(libheom_coo_matrix(H))
119 |
120 | n_noise = len(noises)
121 | self.qme_impl.alloc_noises(n_noise)
122 | self.noises = []
123 | for u in range(n_noise):
124 | gamma = noises[u]["gamma"].astype(self.dtype)
125 | phi_0 = noises[u]["phi_0"].astype(self.dtype)
126 | sigma = noises[u]["sigma"].astype(self.dtype)
127 | s = noises[u]["S"].astype(self.dtype)
128 | a = noises[u]["A"].astype(self.dtype)
129 | S_delta = complex(noises[u]["s_delta"])
130 | self.noises.append(type("noise", (object,),
131 | dict(gamma=gamma,
132 | phi_0=phi_0,
133 | sigma_s=s.T@sigma,
134 | sigma_a=a.T@sigma,
135 | S_delta=S_delta)))
136 | self.qme_impl.set_noise(u,
137 | libheom_coo_matrix(noises[u]["V"].astype(np.complex128)),
138 | libheom_coo_matrix(gamma),
139 | phi_0,
140 | sigma,
141 | libheom_coo_matrix(s),
142 | S_delta,
143 | libheom_coo_matrix(a))
144 | self.qme_impl.set_param(self.engine_impl)
145 |
146 | self.solver_impl = qme_solver.get_class('{solver}_{c_dtype}{c_order}_{engine}', self.config)()
147 |
148 | self.qme_solver_impl = qme_solver.get_class('qme_solver_{c_dtype}{c_order}_{engine}', self.config)(
149 | self.engine_impl,
150 | self.qme_impl,
151 | self.solver_impl
152 | )
153 |
154 | def solve(self, rho, t_list, callback=lambda t: None, **kwargs):
155 | if rho.flags.c_contiguous != self.c_contiguous:
156 | order_H = ORDER_CHAR_NUMPY[self.c_contiguous]
157 | order_rho = ORDER_CHAR_NUMPY[rho.flags.c_contiguous]
158 | raise ValueError(f'The orders of H and rho are inconsistent: {order_H} and {order_rho}')
159 | if rho.dtype != self.dtype:
160 | raise TypeError(f'The types of H and rho are inconsistent: {self.dtype} and {rho.dtype}')
161 | if 'dt' in kwargs:
162 | kwargs = dict(kwargs, dt=kwargs['dt']*calc_unit())
163 | self.qme_solver_impl.solve(rho, np.array(t_list)*calc_unit(), callback, **kwargs)
164 |
165 | @abstractmethod
166 | def storage_size(self):
167 | return 1
168 |
169 |
--------------------------------------------------------------------------------
/pyheom/noise_decomposition.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import scipy as sp
10 | import scipy.sparse
11 | import itertools
12 | from collections import OrderedDict
13 |
14 | from .predefined_noise import *
15 | from .summation_over_poles import *
16 | from .commuting_matrix import *
17 | from .pade_spectral_decomposition import *
18 |
19 | fsd_coeffs = {
20 | 100.0: [[1, 1.35486, 1.34275],
21 | [2, 5.50923, 0.880362],
22 | [3, 0.553793, -0.965783]],
23 | 1000.0: [[1, 79.1394, 0.637942],
24 | [1, 0.655349, 0.666920],
25 | [2, 11.6632, 0.456271],
26 | [2, 1.54597, 0.740457],
27 | [3, 3.39011, 0.626892]],
28 | }
29 |
30 | def calc_S_msd(gamma_k, a_k, T, n_ltc):
31 | def cot(x):
32 | return 1/np.tan(x)
33 |
34 | n_m = a_k.shape[0]
35 | n_k = n_ltc
36 |
37 | nu_k = np.zeros(n_k)
38 | s_k = np.zeros(n_m + n_k, dtype=a_k.dtype)
39 | S_delta = 0.0
40 |
41 | for k in range(n_k):
42 | nu_k[k] = 2*np.pi*(k + 1)*T
43 | if np.any(np.abs(gamma_k - nu_k[k]) < (np.finfo(float).eps)):
44 | raise Exception('[Error] Bath frequency #{} is degenerated.'.format(k))
45 |
46 | # r_k[m] -->
47 | for m in range(n_m):
48 | s_k[m] = -2*a_k[m]*cot(gamma_k[m]/(2*T))/2.0
49 |
50 | for k in range(n_k):
51 | s_k[n_m+k] = 0.0
52 | for m in range(n_m):
53 | s_k[n_m+k] += -4*a_k[m]/(nu_k[k]**2 - gamma_k[m]**2)
54 | s_k[n_m+k] *= T*nu_k[k]
55 |
56 | for m in range(n_m):
57 | inner = 1/gamma_k[m]**2 - 1/(2*T*gamma_k[m])*cot(gamma_k[m]/(2*T))
58 | for k in range(n_k):
59 | inner -= 2/(nu_k[k]**2 - gamma_k[m]**2)
60 | S_delta += -2*T*a_k[m]*inner
61 |
62 | result = OrderedDict()
63 |
64 | def put_coeff(a, m, coeff):
65 | if (a, m) in result:
66 | result[(a, m)] += coeff
67 | else:
68 | result[(a, m)] = coeff
69 |
70 | put_coeff(np.inf, 0, S_delta)
71 | for k in range(n_m):
72 | put_coeff(gamma_k[k], 0, s_k[k])
73 |
74 | for k in range(n_k):
75 | put_coeff(nu_k[k], 0, s_k[k + n_m])
76 |
77 | return result
78 |
79 | def calc_noise_time_domain(J, T, type_ltc, **kwargs):
80 | type_ltc = type_ltc.lower()
81 |
82 | if (type_ltc == 'none'):
83 | n_list = [[0, T, 1, 0]]
84 |
85 | return calc_S_from_poles(J.poles, n_list), calc_A_from_poles(J.poles)
86 |
87 | elif (type_ltc == 'msd'):
88 | n_msd = kwargs['n_msd']
89 |
90 | A = calc_A_from_poles(J.poles)
91 |
92 | gamma_k = np.zeros(len(A), dtype=np.complex128)
93 | a_k = np.zeros(len(A), dtype=np.complex128)
94 | for k, (gamma, l) in enumerate(A.keys()):
95 | if l != 0:
96 | raise Exception('[Error] msd accepts only first-order poles')
97 | gamma_k[k] = gamma
98 | a_k[k] = A[(gamma, 0)]
99 |
100 | return calc_S_msd(gamma_k, a_k, T, n_msd), A
101 |
102 | elif (type_ltc == 'psd' or type_ltc == 'psd+fsd' ):
103 | coeffs = []
104 | coeff_0 = 0
105 |
106 | if type_ltc == 'psd+fsd':
107 | n_fsd_rec = kwargs['n_fsd_rec']
108 | chi_fsd = kwargs['chi_fsd']
109 | # calc fsd coeffs
110 | T_n = T
111 | for i in range(n_fsd_rec):
112 | T_np1 = T_n*chi_fsd
113 | coeff_0 += T_n - T_np1
114 | T_n = T_np1
115 | for j, a, b in fsd_coeffs[chi_fsd]:
116 | coeffs.append([j, a, b, T_n])
117 | T_0 = T_n
118 | else:
119 | T_0 = T
120 |
121 | # calc psd coeffs
122 | n_psd = kwargs['n_psd']
123 | type_psd = kwargs['type_psd']
124 | xi, eta, R_1, T_3 = psd(n_psd, type_psd)
125 |
126 | # collect poles
127 | poles = OrderedDict()
128 | ## psd poles
129 | poles[(0, 1, 0)] = T_0
130 | if (R_1 != 0):
131 | poles[(0, 0, 0)] = R_1
132 | if (T_3 != 0):
133 | poles[(0, 0, 1)] = T_3
134 | for p in range(n_psd):
135 | poles[(T_0*xi[p], 1, 0)] = 2*eta[p]*T_0
136 |
137 | ## fsd poles
138 | poles[(0, 1, 0)] += coeff_0
139 | for j, a, b, T_n in coeffs:
140 | poles[(T_n/a, j, 0)] = b*(T_n/a)**(2*j-1)
141 |
142 | n_list = [[a, b, m, n] for (a, m, n), b in poles.items()]
143 |
144 | return calc_S_from_poles(J.poles, n_list), calc_A_from_poles(J.poles)
145 | else:
146 | raise Exception('[Error] Unknown ltc')
147 |
148 |
149 | def calc_noise_params(S, A):
150 |
151 | # Calculate Basis Degeneracy
152 | phi_deg_dict = OrderedDict()
153 | for gamma, n in itertools.chain(S.keys(), A.keys()):
154 | if (gamma == np.inf):
155 | continue
156 | if gamma in phi_deg_dict:
157 | phi_deg_dict[gamma] = max(phi_deg_dict[gamma], n + 1)
158 | else:
159 | phi_deg_dict[gamma] = n + 1
160 | phi_dim = sum((n for n in phi_deg_dict.values()))
161 |
162 | #
163 | phi = []
164 | phi_0 = np.zeros((phi_dim), np.complex128)
165 | gamma = sp.sparse.lil_matrix((phi_dim, phi_dim), dtype=np.complex128)
166 | sigma = np.ones((phi_dim), np.complex128)
167 |
168 | s_vec = np.zeros((phi_dim), np.complex128)
169 | a_vec = np.zeros((phi_dim), np.complex128)
170 | s_mat = sp.sparse.lil_matrix((phi_dim, phi_dim), dtype=np.complex128)
171 | a_mat = sp.sparse.lil_matrix((phi_dim, phi_dim), dtype=np.complex128)
172 |
173 | ctr = 0
174 | for gamma_n, deg_max in phi_deg_dict.items():
175 | for deg in range(deg_max):
176 | phi.append((gamma_n, deg))
177 | phi_0[ctr] = 1 if deg == 0 else 0
178 | gamma[ctr,ctr] = gamma_n
179 | if deg > 0:
180 | gamma[ctr,ctr-1] = -deg
181 | if ((gamma_n, deg) in S):
182 | s_vec[ctr] = S[(gamma_n, deg)]
183 | if ((gamma_n, deg) in A):
184 | a_vec[ctr] = A[(gamma_n, deg)]
185 | ctr += 1
186 | block_size = deg+1
187 | s_mat[ctr-block_size:ctr, ctr-block_size:ctr] \
188 | = get_commuting_matrix(s_vec[ctr-block_size:ctr],
189 | gamma[ctr-block_size:ctr, ctr-block_size:ctr].todense(),
190 | sigma[ctr-block_size:ctr])
191 | a_mat[ctr-block_size:ctr, ctr-block_size:ctr] \
192 | = get_commuting_matrix(a_vec[ctr-block_size:ctr],
193 | gamma[ctr-block_size:ctr, ctr-block_size:ctr].todense(),
194 | sigma[ctr-block_size:ctr])
195 | S_delta = 0.0
196 | if (np.inf, 0) in S:
197 | S_delta = S[(np.inf, 0)]
198 |
199 | return dict(gamma = gamma,
200 | sigma = sigma,
201 | phi_0 = phi_0,
202 | S = s_mat,
203 | s_delta = S_delta,
204 | A = a_mat)
205 |
206 | def noise_decomposition(J, T, type_ltc, **kwargs):
207 | return calc_noise_params(*calc_noise_time_domain(J, T, type_ltc, **kwargs))
208 |
209 | # noise = calc_noise_params(*calc_noise_time_domain(None, T, 'psd+fsd', n_psd = 1, type_psd = 'N/N', n_fsd_rec=1, chi_fsd=100.0))
210 | # noise = calc_noise_params(*calc_noise_time_domain(J, T, 'psd+fsd',
211 | # n_psd = 1, type_psd = 'N/N',
212 | # n_fsd_rec=1, chi_fsd=100.0))
213 | # noise = calc_noise_params(*calc_noise_time_domain(J, T, 'psd',
214 | # n_psd = 1, type_psd = 'N-1/N'))
215 | # noise = calc_noise_params(*calc_noise_time_domain(J, T, 'msd',
216 | # n_msd = 10))
217 | # noise = calc_noise_params(*calc_noise_time_domain(J, T, 'NONE'))
218 |
--------------------------------------------------------------------------------
/pyheom/summation_over_poles.py:
--------------------------------------------------------------------------------
1 | # -*- mode:python -*-
2 | # PyHEOM
3 | # Copyright (c) Tatsushi Ikeda
4 | # This library is distributed under BSD 3-Clause License.
5 | # See LINCENSE.txt for licence.
6 | # ------------------------------------------------------------------------*/
7 |
8 | import numpy as np
9 | import scipy as sp
10 | import scipy.special
11 | from collections import OrderedDict
12 |
13 | def calc_A_from_poles(poles):
14 | """Calculate:
15 |
16 | .. math:
17 | A(t)=-\frac{1/\pi}\int_{0}^{\infty}d\omega f(\omega )\sin(\omega t),
18 |
19 | where the function f is constructed from poles = [(a, b, m, n), ...] as
20 |
21 | .. math:
22 | f(\omega)=\sum\frac{b\omega^{2n+1}}{(a^2+\omega^2)^m}.
23 |
24 | Here, a != 0 and m > n. m and n should be non-negative integers.
25 |
26 | The result is expressed as an OrderdDict {(a, l): c, ...}, which represents a function
27 |
28 | .. math:
29 | A(t)=\sum c\cdot t^l\exp(-at).
30 |
31 | """
32 | result = OrderedDict()
33 |
34 | def put_coeff(a, m, coeff):
35 | if (a, m) in result:
36 | result[(a, m)] += coeff
37 | else:
38 | result[(a, m)] = coeff
39 |
40 | for j in range(len(poles)):
41 | a, b, m, n = poles[j]
42 |
43 | def sub(a, b, m, n):
44 | for l in range(m):
45 | inner1 = 0
46 | for p in range(min(m - l, 2*n + 2)):
47 | inner1 += (sp.special.binom(m - l - 1, p)
48 | *sp.special.poch(m, m - l - p - 1)
49 | *sp.special.poch(2*(n + 1) - p, p)
50 | *(0.5)**((2*m - l - p - 1))
51 | *(-1)**(p + n + 1))
52 | put_coeff(a, l,
53 | (b/sp.special.factorial(m - 1)
54 | *sp.special.binom(m - 1, l)
55 | *a**(2*(n - m + 1) + l)
56 | *inner1))
57 |
58 | sub(a, b, m, n)
59 | return result
60 |
61 |
62 | def calc_S_from_poles(poles_1, poles_2):
63 | """Calculate:
64 |
65 | .. math:
66 | S(t)=\frac{2/\pi}\int_{0}^{\infty}d\omega f(\omega )g(\omega)\cos(\omega t),
67 |
68 | where the function f and g is constructed from poles_1 = [(a, b, m, n), ...] and poles_2 = [(a', b', m', n'), ...] as
69 |
70 | .. math:
71 | f(\omega)=\sum\frac{b\omega^{2n+1}}{(a^2+\omega^2)^m}
72 |
73 | and
74 |
75 | .. math:
76 | g(\omega)=\sum\frac{b'\omega^{2n'+1}}{(a'^2+\omega^2)^m'}.
77 |
78 | Here, m, n, m', and n should be non-negative integers. Regarding
79 | pole_1, a != 0 and m > n. Regarding pole_2, when a' != 0 then m'
80 | > n'. When a' == 0, (m', n') = (1, 0), (0, 0), or (0, 1).
81 |
82 | The result is expressed as an OrderedDict {(a, l): c, ...}, which represents a function
83 |
84 | .. math:
85 | A(t)=\sum \begin{dcases}
86 | c\cdot 2\delta(t) & (a = \infty~\text{and}~l = 0)\\
87 | c\cdot 2\ddot{\delta}(t) & (a = \infty~\text{and}~l = 2)\\
88 | ct^l\exp(-at) & (\text{otherwise}).
89 | \end{dcases}
90 |
91 | """
92 |
93 | result = OrderedDict()
94 |
95 | def put_coeff(a, m, coeff):
96 | if (a, m) in result:
97 | result[(a, m)] += coeff
98 | else:
99 | result[(a, m)] = coeff
100 |
101 | for a_, b_, m_, n_ in poles_2:
102 | for a, b, m, n in poles_1:
103 | if (a_ == 0 or a == a_):
104 | def sub(a, b, b_, M, N):
105 | if (N == M + 1):
106 | put_coeff(np.inf, 2, -b*b_)
107 | put_coeff(np.inf, 0, -b*b_*a**2*M)
108 | for l in range(M):
109 | inner1 = 0
110 | for r in range(M):
111 | inner2 = 0
112 | for p in range(min(M - l, 2*r + 1)):
113 | inner2 += (sp.special.binom(M - l - 1, p)
114 | *sp.special.poch(M, M - l - p - 1)
115 | *sp.special.poch(2*r - p + 1, p)
116 | *(0.5)**(2*M - l - p - 1)
117 | *(-1)**(r - p + 1))
118 | inner1 += (M - r)*sp.special.binom(M + 1, r)*inner2
119 | put_coeff(a, l,
120 | -sp.special.binom(M - 1, l)
121 | *2*b*b_/sp.special.factorial(M - 1)
122 | *a**(l + 3)
123 | *inner1)
124 | elif (N == M):
125 | put_coeff(np.inf, 0, b*b_)
126 | for l in range(M):
127 | inner1 = 0
128 | for r in range(M):
129 | inner2 = 0
130 | for p in range(min(M - l, 2*r + 1)):
131 | inner2 += (sp.special.binom(M - l - 1, p)
132 | *sp.special.poch(M, M - l - p - 1)
133 | *sp.special.poch(2*r - p + 1, p)
134 | *(0.5)**(2*M - l - p - 1)
135 | *(-1)**(r - p + 1))
136 | inner1 += sp.special.binom(M, r)*inner2
137 | put_coeff(a, l,
138 | sp.special.binom(M - 1, l)
139 | *2*b*b_/sp.special.factorial(M - 1)
140 | *a**(l + 1)
141 | *inner1)
142 | elif (N < M):
143 | for l in range(M):
144 | inner1 = 0
145 | for p in range(min(M - l, 2*N + 1)):
146 | inner1 += (sp.special.binom(M - l - 1, p)
147 | *sp.special.poch(M, M - l - p - 1)
148 | *sp.special.poch(2*N - p + 1, p)
149 | *(0.5)**(2*M - l - p - 1)
150 | *(-1)**(N - p))
151 | put_coeff(a, l,
152 | sp.special.binom(M - 1, l)
153 | *2*b*b_/sp.special.factorial(M - 1)
154 | *a**(2*(N - M) + l + 1)
155 | *inner1)
156 | else:
157 | raise Exception('[Error] An invalid pole is given to calc_S_from_poles')
158 | if (a == a_):
159 | sub(a, b, b_, m + m_, n + n_ + 1)
160 | else:
161 | if (m_ == 1 and n_ == 0):
162 | sub(a, b, b_, m, n)
163 | elif (m_ == 0 and n_ == 0):
164 | sub(a, b, b_, m, n + 1)
165 | elif (m_ == 0 and n_ == 1):
166 | sub(a, b, b_, m, n + 2)
167 | else:
168 | raise Exception('[Error] An invalid pole is given to calc_S_from_poles')
169 | else:
170 | def sub(a, b, m, n, a_, b_, m_, n_):
171 | for l in range(m):
172 | inner1 = 0
173 | for p in range(min(m - l, 2*(n + n_ + 1) + 1)):
174 | inner2 = 0
175 | for q in range(m - l - p):
176 | inner3 = 0
177 | for r in range(q + 1):
178 | inner3 += (sp.special.binom(q, r)
179 | /((a - a_)**r*(a + a_)**(q - r))
180 | *sp.special.poch(m_, q - r)
181 | *sp.special.poch(m_, r)
182 | /(-a**2 + a_**2)**m_)
183 | inner2 += (sp.special.binom(m - l - p - 1, q)
184 | *sp.special.poch(m, m - l - p - q - 1)
185 | /(2*a)**(2*m - l - p - q - 1)
186 | *inner3)
187 | inner1 += (sp.special.binom(m - l - 1, p)
188 | *(-1)**(n + n_ - p - 1)
189 | *sp.special.poch(2*(n + n_ + 1) - p + 1, p)
190 | *(a)**(2*(n + n_ + 1) - p)
191 | *inner2)
192 | put_coeff(a, l,
193 | 2*b*b_
194 | /sp.special.factorial(m - 1)
195 | *sp.special.binom(m - 1, l)
196 | *inner1)
197 | sub(a, b, m, n, a_, b_, m_, n_)
198 | sub(a_, b_, m_, n_, a, b, m, n)
199 | return result
200 |
--------------------------------------------------------------------------------
/src/pylibheom.cc.j2:
--------------------------------------------------------------------------------
1 | /* -*- mode:c++ -*-
2 | * PyHEOM
3 | * Copyright (c) Tatsushi Ikeda
4 | * This library is distributed under BSD 3-Clause License.
5 | * See LINCENSE.txt for licence.
6 | *------------------------------------------------------------------------*/
7 |
8 | #include
9 |
10 | #include "libheom.h"
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | using namespace libheom;
19 | namespace py = pybind11;
20 |
21 | template
22 | using vector_py = py::array_t;
23 |
24 | template
25 | class coo_matrix_py
26 | {
27 | public:
28 | int rows;
29 | int cols;
30 | int nnz;
31 | vector_py row;
32 | vector_py col;
33 | vector_py data;
34 |
35 | coo_matrix_py(int rows, int cols, int nnz,
36 | vector_py row, vector_py col, vector_py data) :
37 | rows(rows), cols(cols), nnz(nnz), row(row), col(col), data(data)
38 | {
39 | CALL_TRACE();
40 | }
41 |
42 | template
43 | void dump(lil_matrix& mat)
44 | {
45 | CALL_TRACE();
46 | mat.clear();
47 | const int* row_ptr = row.data();
48 | const int* col_ptr = col.data();
49 | const dtype* d = data.data();
50 | mat.set_shape(rows, cols);
51 | for (int i = 0; i < nnz; ++i) {
52 | mat.push(row_ptr[i], col_ptr[i], d[i]);
53 | }
54 | }
55 | };
56 |
57 |
58 | template
59 | void set_system_py(qme_base& obj,
60 | coo_matrix_py& H_py)
61 | {
62 | CALL_TRACE();
63 | if (H_py.rows != H_py.cols) {
64 | throw std::runtime_error("[Error] Hamiltonian must be a square matrix");
65 | }
66 | H_py.dump(obj.H);
67 | obj.n_level = H_py.rows;
68 | obj.n_level_2 = H_py.rows*H_py.cols;
69 | }
70 |
71 | template
72 | void alloc_noises_py(qme_base& obj,
73 | int n_noise)
74 | {
75 | CALL_TRACE();
76 | obj.alloc_noises(n_noise);
77 | }
78 |
79 | template
80 | void set_noise_py(qme_base& obj,
81 | int u,
82 | coo_matrix_py& V_py,
83 | coo_matrix_py& gamma_py,
84 | vector_py phi_0,
85 | vector_py sigma,
86 | coo_matrix_py& S_py,
87 | dtype s_delta,
88 | coo_matrix_py& A_py)
89 | {
90 | CALL_TRACE();
91 | if (V_py.rows != V_py.cols) {
92 | throw std::runtime_error("[Error] Noise operator must be a square matrix");
93 | }
94 | if (V_py.rows != obj.n_level) {
95 | throw std::runtime_error("[Error] Hamiltonian and noise operators must have the same dimension");
96 | }
97 |
98 | V_py.dump(obj.V[u]);
99 |
100 | obj.len_gamma[u] = static_cast(gamma_py.rows);
101 | gamma_py.dump(obj.gamma[u]);
102 |
103 | obj.phi_0[u].resize(obj.len_gamma[u]);
104 | std::copy_n(phi_0.data(), phi_0.shape(0), obj.phi_0[u].data());
105 |
106 | obj.sigma[u].resize(obj.len_gamma[u]);
107 | std::copy_n(sigma.data(), sigma.shape(0), obj.sigma[u].data());
108 |
109 | S_py.dump(obj.S[u]);
110 |
111 | obj.s_delta[u] = s_delta;
112 |
113 | A_py.dump(obj.A[u]);
114 | }
115 |
116 |
117 | template
118 | void solve_py(qme_solver& obj,
119 | vector_py rho,
120 | vector_py> t_list,
121 | py::function& callback,
122 | const py::kwargs& kwargs_py)
123 | {
124 | CALL_TRACE();
125 | const auto &buff_info = t_list.request();
126 | const auto &shape = buff_info.shape;
127 | kwargs_t kwargs;
128 |
129 | if (kwargs_py.contains("dt")) {
130 | kwargs.insert(std::make_pair("dt", py::cast>(kwargs_py["dt"])));
131 | }
132 | if (kwargs_py.contains("atol")) {
133 | kwargs.insert(std::make_pair("atol", py::cast>(kwargs_py["atol"])));
134 | }
135 | if (kwargs_py.contains("rtol")) {
136 | kwargs.insert(std::make_pair("rtol", py::cast>(kwargs_py["rtol"])));
137 | }
138 | obj.solve(rho.mutable_data(),
139 | t_list.data(),
140 | shape[0],
141 | [&](real_t t) {
142 | if (PyErr_CheckSignals() != 0) {
143 | throw py::error_already_set();
144 | }
145 | callback(t);
146 | },
147 | kwargs);
148 | }
149 |
150 | // from https://stackoverflow.com/questions/70589954/initialization-and-finalization-of-pybind-module
151 | class Module
152 | {
153 | public:
154 | Module()
155 | {
156 | std::set_terminate(terminate_handler);
157 | struct sigaction action;
158 | memset(&action, 0, sizeof(struct sigaction));
159 | action.sa_flags = SA_SIGINFO;
160 | action.sa_sigaction = sigsegv_handler;
161 | sigaction(SIGSEGV, &action, NULL);
162 | }
163 | ~Module()
164 | {
165 |
166 | }
167 | };
168 |
169 | PYBIND11_MODULE(pylibheom, m)
170 | {
171 | static Module module;
172 |
173 | m.doc() = "document";
174 |
175 | {% for engine in ["eigen", "mkl", "cuda"] %}
176 | m.def("{{engine}}_is_supported", &is_supported<{{engine}}>);
177 | {% endfor %}
178 |
179 | // naming
180 | // qme_zhdr2:
181 | // double precision
182 | // Hilbert space expression
183 | // dense matrix format for operator matrices
184 | // row-major packing for density and operator matrices
185 | // 2-level system
186 | // qme_clscrn:
187 | // single precision
188 | // Liouville space expression
189 | // sparse matrix format for operator and super-operator matrices
190 | // column-major packing for density and operator matrices
191 | // row-major packing for super-operator matrices
192 | // n-level system
193 |
194 | {% if "eigen" in engines %}
195 | py::class_(m, "eigen").def(py::init<>());
196 | {% endif %}
197 | {% if "mkl" in engines %}
198 | py::class_(m, "mkl").def(py::init<>());
199 | {% endif %}
200 | {% if "cuda" in engines %}
201 | py::class_(m, "cuda").def(py::init());
202 | {% endif %}
203 |
204 | {% for dtype, dtype_symbol in types %}
205 | py::class_>(m, "coo_matrix_{{dtype_symbol}}")
206 | .def(py::init,
208 | vector_py,
209 | vector_py<{{dtype}}>>());
210 |
211 | {% for order, order_symbol in orders %}
212 | {% for engine in engines %}
213 | py::class_>(m, "qme_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
214 | .def(py::init<>())
215 | .def("set_system", &set_system_py<{{dtype}},{{order}},{{engine}}>)
216 | .def("alloc_noises", &alloc_noises_py<{{dtype}},{{order}},{{engine}}>)
217 | .def("set_noise", &set_noise_py<{{dtype}},{{order}},{{engine}}>);
218 |
219 | py::class_>(m, "solver_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
220 | .def(py::init<>());
221 |
222 | py::class_>(m, "qme_solver_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
223 | .def(py::init<{{engine}}*, qme_base<{{dtype}},{{order}},{{engine}}>*, solver_base<{{dtype}},{{order}},{{engine}}>*>())
224 | .def("solve", &solve_py<{{dtype}},{{order}},{{engine}}>);
225 |
226 | {% for format, format_symbol in formats %}
227 | {% for num, num_symbol in num_list[engine] %}
228 | // redfield_hilb
229 | py::class_,qme_base<{{dtype}},{{order}},{{engine}}>>(
230 | m, "redfield_{{dtype_symbol}}h{{format_symbol}}{{order_symbol}}{{num_symbol}}_{{engine}}"
231 | )
232 | .def(py::init<>())
233 | .def("set_param", &redfield_hilb<{{num}},{{dtype}},libheom::{{format}},{{order}},{{engine}}>::set_param);
234 |
235 | // redfield_liou
236 | {% for order_liou, order_liou_symbol in orders %}
237 | py::class_,qme_base<{{dtype}},{{order}},{{engine}}>>(
238 | m, "redfield_{{dtype_symbol}}l{{format_symbol}}{{order_symbol}}{{order_liou_symbol}}{{num_symbol}}_{{engine}}"
239 | )
240 | .def(py::init<>())
241 | .def("set_param", &redfield_liou<{{num}},{{dtype}},libheom::{{format}},{{order}},{{order_liou}},{{engine}}>::set_param);
242 | {% endfor %}
243 |
244 | // heom_hilb
245 | py::class_,qme_base<{{dtype}},{{order}},{{engine}}>>(
246 | m, "heom_{{dtype_symbol}}h{{format_symbol}}{{order_symbol}}{{num_symbol}}_{{engine}}"
247 | )
248 | .def(py::init())
249 | .def("set_param", &heom_hilb<{{num}},{{dtype}},libheom::{{format}},{{order}},{{engine}}>::set_param)
250 | .def("get_n_hrchy", &heom<{{dtype}},{{order}},{{engine}}>::get_n_hrchy);
251 |
252 | // heom_liou
253 | {% for order_liou, order_liou_symbol in orders %}
254 | py::class_,qme_base<{{dtype}},{{order}},{{engine}}>>(
255 | m, "heom_{{dtype_symbol}}l{{format_symbol}}{{order_symbol}}{{order_liou_symbol}}{{num_symbol}}_{{engine}}"
256 | )
257 | .def(py::init())
258 | .def("set_param", &heom_liou<{{num}},{{dtype}},libheom::{{format}},{{order}},{{order_liou}},{{engine}}>::set_param)
259 | .def("get_n_hrchy", &heom<{{dtype}},{{order}},{{engine}}>::get_n_hrchy);
260 | {% endfor %}
261 |
262 | // heom_ado
263 | {% for order_liou, order_liou_symbol in orders %}
264 | py::class_,qme_base<{{dtype}},{{order}},{{engine}}>>(
265 | m, "heom_{{dtype_symbol}}a{{format_symbol}}{{order_symbol}}{{order_liou_symbol}}{{num_symbol}}_{{engine}}"
266 | )
267 | .def(py::init())
268 | .def("set_param", &heom_ado<{{num}},{{dtype}},libheom::{{format}},{{order}},{{order_liou}},{{engine}}>::set_param)
269 | .def("get_n_hrchy", &heom<{{dtype}},{{order}},{{engine}}>::get_n_hrchy);
270 | {% endfor %}
271 |
272 | {% endfor %}
273 | {% endfor %}
274 |
275 | py::class_,solver_base<{{dtype}},{{order}},{{engine}}>>(m, "rk4_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
276 | .def(py::init<>());
277 |
278 | py::class_,solver_base<{{dtype}},{{order}},{{engine}}>>(m, "lsrk4_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
279 | .def(py::init<>());
280 |
281 | py::class_,solver_base<{{dtype}},{{order}},{{engine}}>>(m, "rkdp_{{dtype_symbol}}{{order_symbol}}_{{engine}}")
282 | .def(py::init<>());
283 |
284 |
285 | {% endfor %}
286 | {% endfor %}
287 | {% endfor %}
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/examples/pyheom_example_2level_cpu.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "provenance": []
7 | },
8 | "kernelspec": {
9 | "name": "python3",
10 | "display_name": "Python 3"
11 | },
12 | "language_info": {
13 | "name": "python"
14 | },
15 | "gpuClass": "standard",
16 | "widgets": {
17 | "application/vnd.jupyter.widget-state+json": {
18 | "9ad5a007be05432fa24ed2e3aa2102b7": {
19 | "model_module": "@jupyter-widgets/controls",
20 | "model_name": "HBoxModel",
21 | "model_module_version": "1.5.0",
22 | "state": {
23 | "_dom_classes": [],
24 | "_model_module": "@jupyter-widgets/controls",
25 | "_model_module_version": "1.5.0",
26 | "_model_name": "HBoxModel",
27 | "_view_count": null,
28 | "_view_module": "@jupyter-widgets/controls",
29 | "_view_module_version": "1.5.0",
30 | "_view_name": "HBoxView",
31 | "box_style": "",
32 | "children": [
33 | "IPY_MODEL_2acb5efd0d254394a87056334fe05ee7",
34 | "IPY_MODEL_c8c1487ba85e4bc3be775c29d47fb928",
35 | "IPY_MODEL_10706042b33b4a2ea4617a62d454c474"
36 | ],
37 | "layout": "IPY_MODEL_0feec11e8e7c4ea0b370801277c21bf9"
38 | }
39 | },
40 | "2acb5efd0d254394a87056334fe05ee7": {
41 | "model_module": "@jupyter-widgets/controls",
42 | "model_name": "HTMLModel",
43 | "model_module_version": "1.5.0",
44 | "state": {
45 | "_dom_classes": [],
46 | "_model_module": "@jupyter-widgets/controls",
47 | "_model_module_version": "1.5.0",
48 | "_model_name": "HTMLModel",
49 | "_view_count": null,
50 | "_view_module": "@jupyter-widgets/controls",
51 | "_view_module_version": "1.5.0",
52 | "_view_name": "HTMLView",
53 | "description": "",
54 | "description_tooltip": null,
55 | "layout": "IPY_MODEL_f8401ae6d6b446e9962aea165ff42da0",
56 | "placeholder": "",
57 | "style": "IPY_MODEL_d204472652314a7caeca58c2b2307e75",
58 | "value": "100%"
59 | }
60 | },
61 | "c8c1487ba85e4bc3be775c29d47fb928": {
62 | "model_module": "@jupyter-widgets/controls",
63 | "model_name": "FloatProgressModel",
64 | "model_module_version": "1.5.0",
65 | "state": {
66 | "_dom_classes": [],
67 | "_model_module": "@jupyter-widgets/controls",
68 | "_model_module_version": "1.5.0",
69 | "_model_name": "FloatProgressModel",
70 | "_view_count": null,
71 | "_view_module": "@jupyter-widgets/controls",
72 | "_view_module_version": "1.5.0",
73 | "_view_name": "ProgressView",
74 | "bar_style": "success",
75 | "description": "",
76 | "description_tooltip": null,
77 | "layout": "IPY_MODEL_5d21a1b16849496fa75e6f7dac93847a",
78 | "max": 1000,
79 | "min": 0,
80 | "orientation": "horizontal",
81 | "style": "IPY_MODEL_349721d303da4ec597aca9c4ca8f1586",
82 | "value": 1000
83 | }
84 | },
85 | "10706042b33b4a2ea4617a62d454c474": {
86 | "model_module": "@jupyter-widgets/controls",
87 | "model_name": "HTMLModel",
88 | "model_module_version": "1.5.0",
89 | "state": {
90 | "_dom_classes": [],
91 | "_model_module": "@jupyter-widgets/controls",
92 | "_model_module_version": "1.5.0",
93 | "_model_name": "HTMLModel",
94 | "_view_count": null,
95 | "_view_module": "@jupyter-widgets/controls",
96 | "_view_module_version": "1.5.0",
97 | "_view_name": "HTMLView",
98 | "description": "",
99 | "description_tooltip": null,
100 | "layout": "IPY_MODEL_fb616eb0669d4c26b8edf94ab1d4d576",
101 | "placeholder": "",
102 | "style": "IPY_MODEL_fc0ca52fe9ba47798fd9848df7c86e91",
103 | "value": " 1000/1000 [00:02<00:00, 370.76it/s]"
104 | }
105 | },
106 | "0feec11e8e7c4ea0b370801277c21bf9": {
107 | "model_module": "@jupyter-widgets/base",
108 | "model_name": "LayoutModel",
109 | "model_module_version": "1.2.0",
110 | "state": {
111 | "_model_module": "@jupyter-widgets/base",
112 | "_model_module_version": "1.2.0",
113 | "_model_name": "LayoutModel",
114 | "_view_count": null,
115 | "_view_module": "@jupyter-widgets/base",
116 | "_view_module_version": "1.2.0",
117 | "_view_name": "LayoutView",
118 | "align_content": null,
119 | "align_items": null,
120 | "align_self": null,
121 | "border": null,
122 | "bottom": null,
123 | "display": null,
124 | "flex": null,
125 | "flex_flow": null,
126 | "grid_area": null,
127 | "grid_auto_columns": null,
128 | "grid_auto_flow": null,
129 | "grid_auto_rows": null,
130 | "grid_column": null,
131 | "grid_gap": null,
132 | "grid_row": null,
133 | "grid_template_areas": null,
134 | "grid_template_columns": null,
135 | "grid_template_rows": null,
136 | "height": null,
137 | "justify_content": null,
138 | "justify_items": null,
139 | "left": null,
140 | "margin": null,
141 | "max_height": null,
142 | "max_width": null,
143 | "min_height": null,
144 | "min_width": null,
145 | "object_fit": null,
146 | "object_position": null,
147 | "order": null,
148 | "overflow": null,
149 | "overflow_x": null,
150 | "overflow_y": null,
151 | "padding": null,
152 | "right": null,
153 | "top": null,
154 | "visibility": null,
155 | "width": null
156 | }
157 | },
158 | "f8401ae6d6b446e9962aea165ff42da0": {
159 | "model_module": "@jupyter-widgets/base",
160 | "model_name": "LayoutModel",
161 | "model_module_version": "1.2.0",
162 | "state": {
163 | "_model_module": "@jupyter-widgets/base",
164 | "_model_module_version": "1.2.0",
165 | "_model_name": "LayoutModel",
166 | "_view_count": null,
167 | "_view_module": "@jupyter-widgets/base",
168 | "_view_module_version": "1.2.0",
169 | "_view_name": "LayoutView",
170 | "align_content": null,
171 | "align_items": null,
172 | "align_self": null,
173 | "border": null,
174 | "bottom": null,
175 | "display": null,
176 | "flex": null,
177 | "flex_flow": null,
178 | "grid_area": null,
179 | "grid_auto_columns": null,
180 | "grid_auto_flow": null,
181 | "grid_auto_rows": null,
182 | "grid_column": null,
183 | "grid_gap": null,
184 | "grid_row": null,
185 | "grid_template_areas": null,
186 | "grid_template_columns": null,
187 | "grid_template_rows": null,
188 | "height": null,
189 | "justify_content": null,
190 | "justify_items": null,
191 | "left": null,
192 | "margin": null,
193 | "max_height": null,
194 | "max_width": null,
195 | "min_height": null,
196 | "min_width": null,
197 | "object_fit": null,
198 | "object_position": null,
199 | "order": null,
200 | "overflow": null,
201 | "overflow_x": null,
202 | "overflow_y": null,
203 | "padding": null,
204 | "right": null,
205 | "top": null,
206 | "visibility": null,
207 | "width": null
208 | }
209 | },
210 | "d204472652314a7caeca58c2b2307e75": {
211 | "model_module": "@jupyter-widgets/controls",
212 | "model_name": "DescriptionStyleModel",
213 | "model_module_version": "1.5.0",
214 | "state": {
215 | "_model_module": "@jupyter-widgets/controls",
216 | "_model_module_version": "1.5.0",
217 | "_model_name": "DescriptionStyleModel",
218 | "_view_count": null,
219 | "_view_module": "@jupyter-widgets/base",
220 | "_view_module_version": "1.2.0",
221 | "_view_name": "StyleView",
222 | "description_width": ""
223 | }
224 | },
225 | "5d21a1b16849496fa75e6f7dac93847a": {
226 | "model_module": "@jupyter-widgets/base",
227 | "model_name": "LayoutModel",
228 | "model_module_version": "1.2.0",
229 | "state": {
230 | "_model_module": "@jupyter-widgets/base",
231 | "_model_module_version": "1.2.0",
232 | "_model_name": "LayoutModel",
233 | "_view_count": null,
234 | "_view_module": "@jupyter-widgets/base",
235 | "_view_module_version": "1.2.0",
236 | "_view_name": "LayoutView",
237 | "align_content": null,
238 | "align_items": null,
239 | "align_self": null,
240 | "border": null,
241 | "bottom": null,
242 | "display": null,
243 | "flex": null,
244 | "flex_flow": null,
245 | "grid_area": null,
246 | "grid_auto_columns": null,
247 | "grid_auto_flow": null,
248 | "grid_auto_rows": null,
249 | "grid_column": null,
250 | "grid_gap": null,
251 | "grid_row": null,
252 | "grid_template_areas": null,
253 | "grid_template_columns": null,
254 | "grid_template_rows": null,
255 | "height": null,
256 | "justify_content": null,
257 | "justify_items": null,
258 | "left": null,
259 | "margin": null,
260 | "max_height": null,
261 | "max_width": null,
262 | "min_height": null,
263 | "min_width": null,
264 | "object_fit": null,
265 | "object_position": null,
266 | "order": null,
267 | "overflow": null,
268 | "overflow_x": null,
269 | "overflow_y": null,
270 | "padding": null,
271 | "right": null,
272 | "top": null,
273 | "visibility": null,
274 | "width": null
275 | }
276 | },
277 | "349721d303da4ec597aca9c4ca8f1586": {
278 | "model_module": "@jupyter-widgets/controls",
279 | "model_name": "ProgressStyleModel",
280 | "model_module_version": "1.5.0",
281 | "state": {
282 | "_model_module": "@jupyter-widgets/controls",
283 | "_model_module_version": "1.5.0",
284 | "_model_name": "ProgressStyleModel",
285 | "_view_count": null,
286 | "_view_module": "@jupyter-widgets/base",
287 | "_view_module_version": "1.2.0",
288 | "_view_name": "StyleView",
289 | "bar_color": null,
290 | "description_width": ""
291 | }
292 | },
293 | "fb616eb0669d4c26b8edf94ab1d4d576": {
294 | "model_module": "@jupyter-widgets/base",
295 | "model_name": "LayoutModel",
296 | "model_module_version": "1.2.0",
297 | "state": {
298 | "_model_module": "@jupyter-widgets/base",
299 | "_model_module_version": "1.2.0",
300 | "_model_name": "LayoutModel",
301 | "_view_count": null,
302 | "_view_module": "@jupyter-widgets/base",
303 | "_view_module_version": "1.2.0",
304 | "_view_name": "LayoutView",
305 | "align_content": null,
306 | "align_items": null,
307 | "align_self": null,
308 | "border": null,
309 | "bottom": null,
310 | "display": null,
311 | "flex": null,
312 | "flex_flow": null,
313 | "grid_area": null,
314 | "grid_auto_columns": null,
315 | "grid_auto_flow": null,
316 | "grid_auto_rows": null,
317 | "grid_column": null,
318 | "grid_gap": null,
319 | "grid_row": null,
320 | "grid_template_areas": null,
321 | "grid_template_columns": null,
322 | "grid_template_rows": null,
323 | "height": null,
324 | "justify_content": null,
325 | "justify_items": null,
326 | "left": null,
327 | "margin": null,
328 | "max_height": null,
329 | "max_width": null,
330 | "min_height": null,
331 | "min_width": null,
332 | "object_fit": null,
333 | "object_position": null,
334 | "order": null,
335 | "overflow": null,
336 | "overflow_x": null,
337 | "overflow_y": null,
338 | "padding": null,
339 | "right": null,
340 | "top": null,
341 | "visibility": null,
342 | "width": null
343 | }
344 | },
345 | "fc0ca52fe9ba47798fd9848df7c86e91": {
346 | "model_module": "@jupyter-widgets/controls",
347 | "model_name": "DescriptionStyleModel",
348 | "model_module_version": "1.5.0",
349 | "state": {
350 | "_model_module": "@jupyter-widgets/controls",
351 | "_model_module_version": "1.5.0",
352 | "_model_name": "DescriptionStyleModel",
353 | "_view_count": null,
354 | "_view_module": "@jupyter-widgets/base",
355 | "_view_module_version": "1.2.0",
356 | "_view_name": "StyleView",
357 | "description_width": ""
358 | }
359 | },
360 | "08c795f95c4f4ce1a664a11e192a0a3c": {
361 | "model_module": "@jupyter-widgets/controls",
362 | "model_name": "HBoxModel",
363 | "model_module_version": "1.5.0",
364 | "state": {
365 | "_dom_classes": [],
366 | "_model_module": "@jupyter-widgets/controls",
367 | "_model_module_version": "1.5.0",
368 | "_model_name": "HBoxModel",
369 | "_view_count": null,
370 | "_view_module": "@jupyter-widgets/controls",
371 | "_view_module_version": "1.5.0",
372 | "_view_name": "HBoxView",
373 | "box_style": "",
374 | "children": [
375 | "IPY_MODEL_04dffd2ed6fe4bb2b2817c018d233b40",
376 | "IPY_MODEL_604f5d9b321e4e48b65600f7e3c05d60",
377 | "IPY_MODEL_5f89eb2aff234c8098f16032e1fdf539"
378 | ],
379 | "layout": "IPY_MODEL_149a7238cadf4753b6835732bbcdc20c"
380 | }
381 | },
382 | "04dffd2ed6fe4bb2b2817c018d233b40": {
383 | "model_module": "@jupyter-widgets/controls",
384 | "model_name": "HTMLModel",
385 | "model_module_version": "1.5.0",
386 | "state": {
387 | "_dom_classes": [],
388 | "_model_module": "@jupyter-widgets/controls",
389 | "_model_module_version": "1.5.0",
390 | "_model_name": "HTMLModel",
391 | "_view_count": null,
392 | "_view_module": "@jupyter-widgets/controls",
393 | "_view_module_version": "1.5.0",
394 | "_view_name": "HTMLView",
395 | "description": "",
396 | "description_tooltip": null,
397 | "layout": "IPY_MODEL_27111b5e7669437d9b297247173b76fa",
398 | "placeholder": "",
399 | "style": "IPY_MODEL_fde0f49946c648a7a0bca69d536e1269",
400 | "value": "100%"
401 | }
402 | },
403 | "604f5d9b321e4e48b65600f7e3c05d60": {
404 | "model_module": "@jupyter-widgets/controls",
405 | "model_name": "FloatProgressModel",
406 | "model_module_version": "1.5.0",
407 | "state": {
408 | "_dom_classes": [],
409 | "_model_module": "@jupyter-widgets/controls",
410 | "_model_module_version": "1.5.0",
411 | "_model_name": "FloatProgressModel",
412 | "_view_count": null,
413 | "_view_module": "@jupyter-widgets/controls",
414 | "_view_module_version": "1.5.0",
415 | "_view_name": "ProgressView",
416 | "bar_style": "success",
417 | "description": "",
418 | "description_tooltip": null,
419 | "layout": "IPY_MODEL_3d6788b70eba4d38a528df1897f97762",
420 | "max": 1000,
421 | "min": 0,
422 | "orientation": "horizontal",
423 | "style": "IPY_MODEL_99ca30be899b41e5ae80cbd581364dcd",
424 | "value": 1000
425 | }
426 | },
427 | "5f89eb2aff234c8098f16032e1fdf539": {
428 | "model_module": "@jupyter-widgets/controls",
429 | "model_name": "HTMLModel",
430 | "model_module_version": "1.5.0",
431 | "state": {
432 | "_dom_classes": [],
433 | "_model_module": "@jupyter-widgets/controls",
434 | "_model_module_version": "1.5.0",
435 | "_model_name": "HTMLModel",
436 | "_view_count": null,
437 | "_view_module": "@jupyter-widgets/controls",
438 | "_view_module_version": "1.5.0",
439 | "_view_name": "HTMLView",
440 | "description": "",
441 | "description_tooltip": null,
442 | "layout": "IPY_MODEL_1366e1238df843a08bc4f8483e6f5d87",
443 | "placeholder": "",
444 | "style": "IPY_MODEL_c894794a8c834a85b800ac6b81780942",
445 | "value": " 1000/1000 [00:00<00:00, 11400.51it/s]"
446 | }
447 | },
448 | "149a7238cadf4753b6835732bbcdc20c": {
449 | "model_module": "@jupyter-widgets/base",
450 | "model_name": "LayoutModel",
451 | "model_module_version": "1.2.0",
452 | "state": {
453 | "_model_module": "@jupyter-widgets/base",
454 | "_model_module_version": "1.2.0",
455 | "_model_name": "LayoutModel",
456 | "_view_count": null,
457 | "_view_module": "@jupyter-widgets/base",
458 | "_view_module_version": "1.2.0",
459 | "_view_name": "LayoutView",
460 | "align_content": null,
461 | "align_items": null,
462 | "align_self": null,
463 | "border": null,
464 | "bottom": null,
465 | "display": null,
466 | "flex": null,
467 | "flex_flow": null,
468 | "grid_area": null,
469 | "grid_auto_columns": null,
470 | "grid_auto_flow": null,
471 | "grid_auto_rows": null,
472 | "grid_column": null,
473 | "grid_gap": null,
474 | "grid_row": null,
475 | "grid_template_areas": null,
476 | "grid_template_columns": null,
477 | "grid_template_rows": null,
478 | "height": null,
479 | "justify_content": null,
480 | "justify_items": null,
481 | "left": null,
482 | "margin": null,
483 | "max_height": null,
484 | "max_width": null,
485 | "min_height": null,
486 | "min_width": null,
487 | "object_fit": null,
488 | "object_position": null,
489 | "order": null,
490 | "overflow": null,
491 | "overflow_x": null,
492 | "overflow_y": null,
493 | "padding": null,
494 | "right": null,
495 | "top": null,
496 | "visibility": null,
497 | "width": null
498 | }
499 | },
500 | "27111b5e7669437d9b297247173b76fa": {
501 | "model_module": "@jupyter-widgets/base",
502 | "model_name": "LayoutModel",
503 | "model_module_version": "1.2.0",
504 | "state": {
505 | "_model_module": "@jupyter-widgets/base",
506 | "_model_module_version": "1.2.0",
507 | "_model_name": "LayoutModel",
508 | "_view_count": null,
509 | "_view_module": "@jupyter-widgets/base",
510 | "_view_module_version": "1.2.0",
511 | "_view_name": "LayoutView",
512 | "align_content": null,
513 | "align_items": null,
514 | "align_self": null,
515 | "border": null,
516 | "bottom": null,
517 | "display": null,
518 | "flex": null,
519 | "flex_flow": null,
520 | "grid_area": null,
521 | "grid_auto_columns": null,
522 | "grid_auto_flow": null,
523 | "grid_auto_rows": null,
524 | "grid_column": null,
525 | "grid_gap": null,
526 | "grid_row": null,
527 | "grid_template_areas": null,
528 | "grid_template_columns": null,
529 | "grid_template_rows": null,
530 | "height": null,
531 | "justify_content": null,
532 | "justify_items": null,
533 | "left": null,
534 | "margin": null,
535 | "max_height": null,
536 | "max_width": null,
537 | "min_height": null,
538 | "min_width": null,
539 | "object_fit": null,
540 | "object_position": null,
541 | "order": null,
542 | "overflow": null,
543 | "overflow_x": null,
544 | "overflow_y": null,
545 | "padding": null,
546 | "right": null,
547 | "top": null,
548 | "visibility": null,
549 | "width": null
550 | }
551 | },
552 | "fde0f49946c648a7a0bca69d536e1269": {
553 | "model_module": "@jupyter-widgets/controls",
554 | "model_name": "DescriptionStyleModel",
555 | "model_module_version": "1.5.0",
556 | "state": {
557 | "_model_module": "@jupyter-widgets/controls",
558 | "_model_module_version": "1.5.0",
559 | "_model_name": "DescriptionStyleModel",
560 | "_view_count": null,
561 | "_view_module": "@jupyter-widgets/base",
562 | "_view_module_version": "1.2.0",
563 | "_view_name": "StyleView",
564 | "description_width": ""
565 | }
566 | },
567 | "3d6788b70eba4d38a528df1897f97762": {
568 | "model_module": "@jupyter-widgets/base",
569 | "model_name": "LayoutModel",
570 | "model_module_version": "1.2.0",
571 | "state": {
572 | "_model_module": "@jupyter-widgets/base",
573 | "_model_module_version": "1.2.0",
574 | "_model_name": "LayoutModel",
575 | "_view_count": null,
576 | "_view_module": "@jupyter-widgets/base",
577 | "_view_module_version": "1.2.0",
578 | "_view_name": "LayoutView",
579 | "align_content": null,
580 | "align_items": null,
581 | "align_self": null,
582 | "border": null,
583 | "bottom": null,
584 | "display": null,
585 | "flex": null,
586 | "flex_flow": null,
587 | "grid_area": null,
588 | "grid_auto_columns": null,
589 | "grid_auto_flow": null,
590 | "grid_auto_rows": null,
591 | "grid_column": null,
592 | "grid_gap": null,
593 | "grid_row": null,
594 | "grid_template_areas": null,
595 | "grid_template_columns": null,
596 | "grid_template_rows": null,
597 | "height": null,
598 | "justify_content": null,
599 | "justify_items": null,
600 | "left": null,
601 | "margin": null,
602 | "max_height": null,
603 | "max_width": null,
604 | "min_height": null,
605 | "min_width": null,
606 | "object_fit": null,
607 | "object_position": null,
608 | "order": null,
609 | "overflow": null,
610 | "overflow_x": null,
611 | "overflow_y": null,
612 | "padding": null,
613 | "right": null,
614 | "top": null,
615 | "visibility": null,
616 | "width": null
617 | }
618 | },
619 | "99ca30be899b41e5ae80cbd581364dcd": {
620 | "model_module": "@jupyter-widgets/controls",
621 | "model_name": "ProgressStyleModel",
622 | "model_module_version": "1.5.0",
623 | "state": {
624 | "_model_module": "@jupyter-widgets/controls",
625 | "_model_module_version": "1.5.0",
626 | "_model_name": "ProgressStyleModel",
627 | "_view_count": null,
628 | "_view_module": "@jupyter-widgets/base",
629 | "_view_module_version": "1.2.0",
630 | "_view_name": "StyleView",
631 | "bar_color": null,
632 | "description_width": ""
633 | }
634 | },
635 | "1366e1238df843a08bc4f8483e6f5d87": {
636 | "model_module": "@jupyter-widgets/base",
637 | "model_name": "LayoutModel",
638 | "model_module_version": "1.2.0",
639 | "state": {
640 | "_model_module": "@jupyter-widgets/base",
641 | "_model_module_version": "1.2.0",
642 | "_model_name": "LayoutModel",
643 | "_view_count": null,
644 | "_view_module": "@jupyter-widgets/base",
645 | "_view_module_version": "1.2.0",
646 | "_view_name": "LayoutView",
647 | "align_content": null,
648 | "align_items": null,
649 | "align_self": null,
650 | "border": null,
651 | "bottom": null,
652 | "display": null,
653 | "flex": null,
654 | "flex_flow": null,
655 | "grid_area": null,
656 | "grid_auto_columns": null,
657 | "grid_auto_flow": null,
658 | "grid_auto_rows": null,
659 | "grid_column": null,
660 | "grid_gap": null,
661 | "grid_row": null,
662 | "grid_template_areas": null,
663 | "grid_template_columns": null,
664 | "grid_template_rows": null,
665 | "height": null,
666 | "justify_content": null,
667 | "justify_items": null,
668 | "left": null,
669 | "margin": null,
670 | "max_height": null,
671 | "max_width": null,
672 | "min_height": null,
673 | "min_width": null,
674 | "object_fit": null,
675 | "object_position": null,
676 | "order": null,
677 | "overflow": null,
678 | "overflow_x": null,
679 | "overflow_y": null,
680 | "padding": null,
681 | "right": null,
682 | "top": null,
683 | "visibility": null,
684 | "width": null
685 | }
686 | },
687 | "c894794a8c834a85b800ac6b81780942": {
688 | "model_module": "@jupyter-widgets/controls",
689 | "model_name": "DescriptionStyleModel",
690 | "model_module_version": "1.5.0",
691 | "state": {
692 | "_model_module": "@jupyter-widgets/controls",
693 | "_model_module_version": "1.5.0",
694 | "_model_name": "DescriptionStyleModel",
695 | "_view_count": null,
696 | "_view_module": "@jupyter-widgets/base",
697 | "_view_module_version": "1.2.0",
698 | "_view_name": "StyleView",
699 | "description_width": ""
700 | }
701 | }
702 | }
703 | }
704 | },
705 | "cells": [
706 | {
707 | "cell_type": "code",
708 | "execution_count": 1,
709 | "metadata": {
710 | "colab": {
711 | "base_uri": "https://localhost:8080/"
712 | },
713 | "id": "5lvt7IS0CyCY",
714 | "outputId": "18b17c7d-5725-4e40-d2c9-7ca73c708229"
715 | },
716 | "outputs": [
717 | {
718 | "output_type": "stream",
719 | "name": "stdout",
720 | "text": [
721 | "Using pip 22.0.4 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)\n",
722 | "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
723 | "Collecting git+https://github.com/tatsushi-ikeda/pyheom.git@master\n",
724 | " Cloning https://github.com/tatsushi-ikeda/pyheom.git (to revision master) to /tmp/pip-req-build-r6t4_saj\n",
725 | " Running command git version\n",
726 | " git version 2.25.1\n",
727 | " Running command git clone --filter=blob:none https://github.com/tatsushi-ikeda/pyheom.git /tmp/pip-req-build-r6t4_saj\n",
728 | " Cloning into '/tmp/pip-req-build-r6t4_saj'...\n",
729 | " Running command git show-ref master\n",
730 | " 9b084f3d8dc4edffb97d8d961bd5038f3aead919 refs/heads/master\n",
731 | " 9b084f3d8dc4edffb97d8d961bd5038f3aead919 refs/remotes/origin/master\n",
732 | " Running command git symbolic-ref -q HEAD\n",
733 | " refs/heads/master\n",
734 | " Resolved https://github.com/tatsushi-ikeda/pyheom.git to commit 9b084f3d8dc4edffb97d8d961bd5038f3aead919\n",
735 | " Running command git submodule update --init --recursive -q\n",
736 | " Running command python setup.py egg_info\n",
737 | " running egg_info\n",
738 | " creating /tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info\n",
739 | " writing /tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/PKG-INFO\n",
740 | " writing dependency_links to /tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/dependency_links.txt\n",
741 | " writing requirements to /tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/requires.txt\n",
742 | " writing top-level names to /tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/top_level.txt\n",
743 | " writing manifest file '/tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/SOURCES.txt'\n",
744 | " adding license file 'LICENSE.txt'\n",
745 | " writing manifest file '/tmp/pip-pip-egg-info-10c_5o4o/pyheom.egg-info/SOURCES.txt'\n",
746 | " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
747 | "Requirement already satisfied: numpy in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (1.21.6)\n",
748 | "Requirement already satisfied: scipy in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (1.7.3)\n",
749 | "Requirement already satisfied: jinja2 in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (2.11.3)\n",
750 | "Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.8/dist-packages (from jinja2->pyheom==1.0.0a2) (2.0.1)\n"
751 | ]
752 | }
753 | ],
754 | "source": [
755 | "! CMAKE_ARGS=\"-DLIBHEOM_ENABLE_CUDA=OFF -DCMAKE_VERBOSE_MAKEFILE=ON\" pip install git+https://github.com/tatsushi-ikeda/pyheom.git@master -v"
756 | ]
757 | },
758 | {
759 | "cell_type": "code",
760 | "source": [
761 | "import numpy as np\n",
762 | "import scipy as sp\n",
763 | "import scipy.sparse\n",
764 | "\n",
765 | "from sys import stdout, stderr\n",
766 | "import time\n",
767 | "\n",
768 | "import pyheom\n",
769 | "from pyheom import heom_solver, redfield_solver, noise_decomposition, drude, unit\n",
770 | "pyheom.units['energy'] = unit.wavenumber\n",
771 | "pyheom.units['time'] = unit.femtosecond\n",
772 | "from tqdm.auto import tqdm\n",
773 | "\n",
774 | "dtype = np.complex128\n",
775 | "\n",
776 | "epsilon_1 = 100.0\n",
777 | "epsilon_2 = 0.0\n",
778 | "J_12 = 100.0\n",
779 | "gamma = 53.08\n",
780 | "beta = 1/208.51\n",
781 | "lambda_c = J_12/5.0\n",
782 | "\n",
783 | "callback_interval = 1\n",
784 | "count = 1000\n",
785 | "t_list = np.arange(0, count, callback_interval)\n",
786 | "solver_params = dict(\n",
787 | " dt = 5e-2,\n",
788 | " # atol=1e-6, rtol=1e-3\n",
789 | ")\n",
790 | "# \n",
791 | "\n",
792 | "J_1 = drude(eta = 2*lambda_c/gamma, gamma_c = gamma)\n",
793 | "J_2 = drude(eta = 2*lambda_c/gamma, gamma_c = gamma)\n",
794 | "corr_dict_1 = noise_decomposition(\n",
795 | " J_1,\n",
796 | " T = 1/beta,\n",
797 | " type_ltc = 'none'\n",
798 | ")\n",
799 | "corr_dict_2 = noise_decomposition(\n",
800 | " J_2,\n",
801 | " T = 1/beta,\n",
802 | " type_ltc = 'none'\n",
803 | ")\n",
804 | "\n",
805 | "n_level = 2\n",
806 | "depth = 20\n",
807 | "\n",
808 | "H = np.array([[epsilon_1+lambda_c, J_12],\n",
809 | " [J_12, epsilon_2+lambda_c]],\n",
810 | " dtype=dtype)\n",
811 | "\n",
812 | "V_1 = np.array([[1, 0],\n",
813 | " [0, 0]],\n",
814 | " dtype=dtype)\n",
815 | "\n",
816 | "V_2 = np.array([[0, 0],\n",
817 | " [0, 1]],\n",
818 | " dtype=dtype)\n"
819 | ],
820 | "metadata": {
821 | "id": "g8NYq_4a3Cqn"
822 | },
823 | "execution_count": 2,
824 | "outputs": []
825 | },
826 | {
827 | "cell_type": "code",
828 | "source": [
829 | "print(corr_dict_1)\n",
830 | "# Symmetrized correlation function : sigma.T@S@exp(-gamma*t)@phi_0 + s_delta*2*delta(t)\n",
831 | "# Anti-symmetrized correlation function : sigma.T@A@exp(-gamma*t)@phi_0\n",
832 | "# If you want to use custom correlation functions, specify the above parameters directly.\n",
833 | "# Because gamma, S, and A are matrices and phi_0 and sigma are vectors, sum of exponential functions (diagonal gamma) and non-exponential funcitons (non-diagonal gamma) can be written in the above format."
834 | ],
835 | "metadata": {
836 | "colab": {
837 | "base_uri": "https://localhost:8080/"
838 | },
839 | "id": "E6GLBAi4ET2K",
840 | "outputId": "32701cef-8a2d-4528-d62c-0b0eff20aacd"
841 | },
842 | "execution_count": 3,
843 | "outputs": [
844 | {
845 | "output_type": "stream",
846 | "name": "stdout",
847 | "text": [
848 | "{'gamma': <1x1 sparse matrix of type ''\n",
849 | "\twith 1 stored elements in List of Lists format>, 'sigma': array([1.+0.j]), 'phi_0': array([1.+0.j]), 'S': <1x1 sparse matrix of type ''\n",
850 | "\twith 1 stored elements in List of Lists format>, 's_delta': 0.0, 'A': <1x1 sparse matrix of type ''\n",
851 | "\twith 1 stored elements in List of Lists format>}\n"
852 | ]
853 | }
854 | ]
855 | },
856 | {
857 | "cell_type": "code",
858 | "source": [
859 | "space = 'ado' # 'hilbert', 'liouville'\n",
860 | "format = 'sparse' # 'dense'\n",
861 | "engine = 'eigen' # 'cuda', 'mkl'\n",
862 | "solver = 'lsrk4'\n",
863 | "order_liouville = 'row_major' # 'col_major'\n",
864 | "space = 'ado'\n",
865 | "# The above configurations strongly affect the numerical performance of the computation.\n",
866 | "# The optimal choice depends on the sizes of the system and hierarchy space.\n",
867 | "\n",
868 | "qme = heom_solver(H, [dict(V=V_1, **corr_dict_1), dict(V=V_2, **corr_dict_2)],\n",
869 | " space=space, format=format, engine=engine,\n",
870 | " order_liouville=order_liouville,\n",
871 | " solver=solver,\n",
872 | " engine_args=dict(),\n",
873 | " depth = depth)\n",
874 | "\n",
875 | "n_storage = qme.storage_size()\n",
876 | "\n",
877 | "rho = np.zeros((n_storage,n_level,n_level), dtype=dtype)\n",
878 | "rho_0 = rho[0,:,:]\n",
879 | "rho_0[0,0] = 1\n",
880 | "\n",
881 | "pop_save_heom = np.zeros((t_list.shape[0],))\n",
882 | "count = 0\n",
883 | "\n",
884 | "with tqdm(total=t_list.shape[0]) as bar:\n",
885 | " def callback(t):\n",
886 | " global count\n",
887 | " bar.update(1)\n",
888 | " pop_save_heom[count] = rho_0[0,0].real\n",
889 | " count += 1\n",
890 | " begin = time.time()\n",
891 | " qme.solve(rho, t_list, callback=callback, **solver_params)\n",
892 | " end = time.time()\n",
893 | "print('elapsed:', end - begin, file=stderr)\n",
894 | "del qme"
895 | ],
896 | "metadata": {
897 | "id": "mk70vMXqEAvm",
898 | "colab": {
899 | "base_uri": "https://localhost:8080/",
900 | "height": 67,
901 | "referenced_widgets": [
902 | "9ad5a007be05432fa24ed2e3aa2102b7",
903 | "2acb5efd0d254394a87056334fe05ee7",
904 | "c8c1487ba85e4bc3be775c29d47fb928",
905 | "10706042b33b4a2ea4617a62d454c474",
906 | "0feec11e8e7c4ea0b370801277c21bf9",
907 | "f8401ae6d6b446e9962aea165ff42da0",
908 | "d204472652314a7caeca58c2b2307e75",
909 | "5d21a1b16849496fa75e6f7dac93847a",
910 | "349721d303da4ec597aca9c4ca8f1586",
911 | "fb616eb0669d4c26b8edf94ab1d4d576",
912 | "fc0ca52fe9ba47798fd9848df7c86e91"
913 | ]
914 | },
915 | "outputId": "a1a3035e-6993-44c4-ba1f-4e9c1e84b30a"
916 | },
917 | "execution_count": 4,
918 | "outputs": [
919 | {
920 | "output_type": "display_data",
921 | "data": {
922 | "text/plain": [
923 | " 0%| | 0/1000 [00:00, ?it/s]"
924 | ],
925 | "application/vnd.jupyter.widget-view+json": {
926 | "version_major": 2,
927 | "version_minor": 0,
928 | "model_id": "9ad5a007be05432fa24ed2e3aa2102b7"
929 | }
930 | },
931 | "metadata": {}
932 | },
933 | {
934 | "output_type": "stream",
935 | "name": "stderr",
936 | "text": [
937 | "elapsed: 2.633775234222412\n"
938 | ]
939 | }
940 | ]
941 | },
942 | {
943 | "cell_type": "code",
944 | "source": [
945 | "space='liouville'\n",
946 | "qme = redfield_solver(H, [dict(V=V_1, **corr_dict_1), dict(V=V_2, **corr_dict_2)],\n",
947 | " space=space, format=format, engine=engine,\n",
948 | " order_liouville=order_liouville,\n",
949 | " solver=solver,\n",
950 | " engine_args=dict())\n",
951 | "\n",
952 | "n_storage = qme.storage_size()\n",
953 | "\n",
954 | "rho = np.zeros((n_storage,n_level,n_level), dtype=dtype)\n",
955 | "rho_0 = rho[0,:,:]\n",
956 | "rho_0[0,0] = 1\n",
957 | "\n",
958 | "pop_save_rf = np.zeros((t_list.shape[0],))\n",
959 | "count = 0\n",
960 | "\n",
961 | "with tqdm(total=t_list.shape[0]) as bar:\n",
962 | " def callback(t):\n",
963 | " global count\n",
964 | " bar.update(1)\n",
965 | " pop_save_rf[count] = rho_0[0,0].real\n",
966 | " count += 1\n",
967 | " begin = time.time()\n",
968 | " qme.solve(rho, t_list, callback=callback, **solver_params)\n",
969 | " end = time.time()\n",
970 | "print('elapsed:', end - begin, file=stderr)\n",
971 | "del qme"
972 | ],
973 | "metadata": {
974 | "colab": {
975 | "base_uri": "https://localhost:8080/",
976 | "height": 67,
977 | "referenced_widgets": [
978 | "08c795f95c4f4ce1a664a11e192a0a3c",
979 | "04dffd2ed6fe4bb2b2817c018d233b40",
980 | "604f5d9b321e4e48b65600f7e3c05d60",
981 | "5f89eb2aff234c8098f16032e1fdf539",
982 | "149a7238cadf4753b6835732bbcdc20c",
983 | "27111b5e7669437d9b297247173b76fa",
984 | "fde0f49946c648a7a0bca69d536e1269",
985 | "3d6788b70eba4d38a528df1897f97762",
986 | "99ca30be899b41e5ae80cbd581364dcd",
987 | "1366e1238df843a08bc4f8483e6f5d87",
988 | "c894794a8c834a85b800ac6b81780942"
989 | ]
990 | },
991 | "id": "EkctGnOCvjvR",
992 | "outputId": "a1183551-ea96-455f-c7b0-c52f45cf6f25"
993 | },
994 | "execution_count": 5,
995 | "outputs": [
996 | {
997 | "output_type": "display_data",
998 | "data": {
999 | "text/plain": [
1000 | " 0%| | 0/1000 [00:00, ?it/s]"
1001 | ],
1002 | "application/vnd.jupyter.widget-view+json": {
1003 | "version_major": 2,
1004 | "version_minor": 0,
1005 | "model_id": "08c795f95c4f4ce1a664a11e192a0a3c"
1006 | }
1007 | },
1008 | "metadata": {}
1009 | },
1010 | {
1011 | "output_type": "stream",
1012 | "name": "stderr",
1013 | "text": [
1014 | "elapsed: 0.02464151382446289\n"
1015 | ]
1016 | }
1017 | ]
1018 | },
1019 | {
1020 | "cell_type": "code",
1021 | "source": [
1022 | "import matplotlib.pyplot as plt\n",
1023 | "fig = plt.figure(figsize=(6, 4))\n",
1024 | "ax = fig.add_subplot(111, \n",
1025 | " xlabel='time / fs', \n",
1026 | " ylabel='population')\n",
1027 | "ax.plot(t_list, pop_save_heom)\n",
1028 | "ax.plot(t_list, pop_save_rf)"
1029 | ],
1030 | "metadata": {
1031 | "colab": {
1032 | "base_uri": "https://localhost:8080/",
1033 | "height": 297
1034 | },
1035 | "id": "oQCDwnSmJvqD",
1036 | "outputId": "76d54d35-a1f6-4110-8909-89c5ec543692"
1037 | },
1038 | "execution_count": 6,
1039 | "outputs": [
1040 | {
1041 | "output_type": "execute_result",
1042 | "data": {
1043 | "text/plain": [
1044 | "[]"
1045 | ]
1046 | },
1047 | "metadata": {},
1048 | "execution_count": 6
1049 | },
1050 | {
1051 | "output_type": "display_data",
1052 | "data": {
1053 | "text/plain": [
1054 | ""
1055 | ],
1056 | "image/png": "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\n"
1057 | },
1058 | "metadata": {
1059 | "needs_background": "light"
1060 | }
1061 | }
1062 | ]
1063 | }
1064 | ]
1065 | }
--------------------------------------------------------------------------------
/examples/pyheom_example_2level_gpu.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "provenance": []
7 | },
8 | "kernelspec": {
9 | "name": "python3",
10 | "display_name": "Python 3"
11 | },
12 | "language_info": {
13 | "name": "python"
14 | },
15 | "gpuClass": "standard",
16 | "widgets": {
17 | "application/vnd.jupyter.widget-state+json": {
18 | "a7348b593dc9492ba39ddc070f8309cc": {
19 | "model_module": "@jupyter-widgets/controls",
20 | "model_name": "HBoxModel",
21 | "model_module_version": "1.5.0",
22 | "state": {
23 | "_dom_classes": [],
24 | "_model_module": "@jupyter-widgets/controls",
25 | "_model_module_version": "1.5.0",
26 | "_model_name": "HBoxModel",
27 | "_view_count": null,
28 | "_view_module": "@jupyter-widgets/controls",
29 | "_view_module_version": "1.5.0",
30 | "_view_name": "HBoxView",
31 | "box_style": "",
32 | "children": [
33 | "IPY_MODEL_82aaefaf9e5649e5be9f0beef6e9af55",
34 | "IPY_MODEL_ecf40304e63240dab9bb9d013cb27916",
35 | "IPY_MODEL_19808be485524fb48d4576dc1f26e320"
36 | ],
37 | "layout": "IPY_MODEL_71b0e54590204030acd0247529e3014a"
38 | }
39 | },
40 | "82aaefaf9e5649e5be9f0beef6e9af55": {
41 | "model_module": "@jupyter-widgets/controls",
42 | "model_name": "HTMLModel",
43 | "model_module_version": "1.5.0",
44 | "state": {
45 | "_dom_classes": [],
46 | "_model_module": "@jupyter-widgets/controls",
47 | "_model_module_version": "1.5.0",
48 | "_model_name": "HTMLModel",
49 | "_view_count": null,
50 | "_view_module": "@jupyter-widgets/controls",
51 | "_view_module_version": "1.5.0",
52 | "_view_name": "HTMLView",
53 | "description": "",
54 | "description_tooltip": null,
55 | "layout": "IPY_MODEL_4e676805dfc34235b8d40a9a3fe1ecbb",
56 | "placeholder": "",
57 | "style": "IPY_MODEL_cc0f24a6e2204fe2941cfd8215838e2a",
58 | "value": "100%"
59 | }
60 | },
61 | "ecf40304e63240dab9bb9d013cb27916": {
62 | "model_module": "@jupyter-widgets/controls",
63 | "model_name": "FloatProgressModel",
64 | "model_module_version": "1.5.0",
65 | "state": {
66 | "_dom_classes": [],
67 | "_model_module": "@jupyter-widgets/controls",
68 | "_model_module_version": "1.5.0",
69 | "_model_name": "FloatProgressModel",
70 | "_view_count": null,
71 | "_view_module": "@jupyter-widgets/controls",
72 | "_view_module_version": "1.5.0",
73 | "_view_name": "ProgressView",
74 | "bar_style": "success",
75 | "description": "",
76 | "description_tooltip": null,
77 | "layout": "IPY_MODEL_7315beb3a1274fe3b3013bd5bc436c4d",
78 | "max": 1000,
79 | "min": 0,
80 | "orientation": "horizontal",
81 | "style": "IPY_MODEL_02f2a5e6658d4cbba01176fbd0bb95cf",
82 | "value": 1000
83 | }
84 | },
85 | "19808be485524fb48d4576dc1f26e320": {
86 | "model_module": "@jupyter-widgets/controls",
87 | "model_name": "HTMLModel",
88 | "model_module_version": "1.5.0",
89 | "state": {
90 | "_dom_classes": [],
91 | "_model_module": "@jupyter-widgets/controls",
92 | "_model_module_version": "1.5.0",
93 | "_model_name": "HTMLModel",
94 | "_view_count": null,
95 | "_view_module": "@jupyter-widgets/controls",
96 | "_view_module_version": "1.5.0",
97 | "_view_name": "HTMLView",
98 | "description": "",
99 | "description_tooltip": null,
100 | "layout": "IPY_MODEL_3401d2a346214667a76f63fc38286a93",
101 | "placeholder": "",
102 | "style": "IPY_MODEL_b0e130016a6a4ddfa52d25e01543d7b5",
103 | "value": " 1000/1000 [00:02<00:00, 393.96it/s]"
104 | }
105 | },
106 | "71b0e54590204030acd0247529e3014a": {
107 | "model_module": "@jupyter-widgets/base",
108 | "model_name": "LayoutModel",
109 | "model_module_version": "1.2.0",
110 | "state": {
111 | "_model_module": "@jupyter-widgets/base",
112 | "_model_module_version": "1.2.0",
113 | "_model_name": "LayoutModel",
114 | "_view_count": null,
115 | "_view_module": "@jupyter-widgets/base",
116 | "_view_module_version": "1.2.0",
117 | "_view_name": "LayoutView",
118 | "align_content": null,
119 | "align_items": null,
120 | "align_self": null,
121 | "border": null,
122 | "bottom": null,
123 | "display": null,
124 | "flex": null,
125 | "flex_flow": null,
126 | "grid_area": null,
127 | "grid_auto_columns": null,
128 | "grid_auto_flow": null,
129 | "grid_auto_rows": null,
130 | "grid_column": null,
131 | "grid_gap": null,
132 | "grid_row": null,
133 | "grid_template_areas": null,
134 | "grid_template_columns": null,
135 | "grid_template_rows": null,
136 | "height": null,
137 | "justify_content": null,
138 | "justify_items": null,
139 | "left": null,
140 | "margin": null,
141 | "max_height": null,
142 | "max_width": null,
143 | "min_height": null,
144 | "min_width": null,
145 | "object_fit": null,
146 | "object_position": null,
147 | "order": null,
148 | "overflow": null,
149 | "overflow_x": null,
150 | "overflow_y": null,
151 | "padding": null,
152 | "right": null,
153 | "top": null,
154 | "visibility": null,
155 | "width": null
156 | }
157 | },
158 | "4e676805dfc34235b8d40a9a3fe1ecbb": {
159 | "model_module": "@jupyter-widgets/base",
160 | "model_name": "LayoutModel",
161 | "model_module_version": "1.2.0",
162 | "state": {
163 | "_model_module": "@jupyter-widgets/base",
164 | "_model_module_version": "1.2.0",
165 | "_model_name": "LayoutModel",
166 | "_view_count": null,
167 | "_view_module": "@jupyter-widgets/base",
168 | "_view_module_version": "1.2.0",
169 | "_view_name": "LayoutView",
170 | "align_content": null,
171 | "align_items": null,
172 | "align_self": null,
173 | "border": null,
174 | "bottom": null,
175 | "display": null,
176 | "flex": null,
177 | "flex_flow": null,
178 | "grid_area": null,
179 | "grid_auto_columns": null,
180 | "grid_auto_flow": null,
181 | "grid_auto_rows": null,
182 | "grid_column": null,
183 | "grid_gap": null,
184 | "grid_row": null,
185 | "grid_template_areas": null,
186 | "grid_template_columns": null,
187 | "grid_template_rows": null,
188 | "height": null,
189 | "justify_content": null,
190 | "justify_items": null,
191 | "left": null,
192 | "margin": null,
193 | "max_height": null,
194 | "max_width": null,
195 | "min_height": null,
196 | "min_width": null,
197 | "object_fit": null,
198 | "object_position": null,
199 | "order": null,
200 | "overflow": null,
201 | "overflow_x": null,
202 | "overflow_y": null,
203 | "padding": null,
204 | "right": null,
205 | "top": null,
206 | "visibility": null,
207 | "width": null
208 | }
209 | },
210 | "cc0f24a6e2204fe2941cfd8215838e2a": {
211 | "model_module": "@jupyter-widgets/controls",
212 | "model_name": "DescriptionStyleModel",
213 | "model_module_version": "1.5.0",
214 | "state": {
215 | "_model_module": "@jupyter-widgets/controls",
216 | "_model_module_version": "1.5.0",
217 | "_model_name": "DescriptionStyleModel",
218 | "_view_count": null,
219 | "_view_module": "@jupyter-widgets/base",
220 | "_view_module_version": "1.2.0",
221 | "_view_name": "StyleView",
222 | "description_width": ""
223 | }
224 | },
225 | "7315beb3a1274fe3b3013bd5bc436c4d": {
226 | "model_module": "@jupyter-widgets/base",
227 | "model_name": "LayoutModel",
228 | "model_module_version": "1.2.0",
229 | "state": {
230 | "_model_module": "@jupyter-widgets/base",
231 | "_model_module_version": "1.2.0",
232 | "_model_name": "LayoutModel",
233 | "_view_count": null,
234 | "_view_module": "@jupyter-widgets/base",
235 | "_view_module_version": "1.2.0",
236 | "_view_name": "LayoutView",
237 | "align_content": null,
238 | "align_items": null,
239 | "align_self": null,
240 | "border": null,
241 | "bottom": null,
242 | "display": null,
243 | "flex": null,
244 | "flex_flow": null,
245 | "grid_area": null,
246 | "grid_auto_columns": null,
247 | "grid_auto_flow": null,
248 | "grid_auto_rows": null,
249 | "grid_column": null,
250 | "grid_gap": null,
251 | "grid_row": null,
252 | "grid_template_areas": null,
253 | "grid_template_columns": null,
254 | "grid_template_rows": null,
255 | "height": null,
256 | "justify_content": null,
257 | "justify_items": null,
258 | "left": null,
259 | "margin": null,
260 | "max_height": null,
261 | "max_width": null,
262 | "min_height": null,
263 | "min_width": null,
264 | "object_fit": null,
265 | "object_position": null,
266 | "order": null,
267 | "overflow": null,
268 | "overflow_x": null,
269 | "overflow_y": null,
270 | "padding": null,
271 | "right": null,
272 | "top": null,
273 | "visibility": null,
274 | "width": null
275 | }
276 | },
277 | "02f2a5e6658d4cbba01176fbd0bb95cf": {
278 | "model_module": "@jupyter-widgets/controls",
279 | "model_name": "ProgressStyleModel",
280 | "model_module_version": "1.5.0",
281 | "state": {
282 | "_model_module": "@jupyter-widgets/controls",
283 | "_model_module_version": "1.5.0",
284 | "_model_name": "ProgressStyleModel",
285 | "_view_count": null,
286 | "_view_module": "@jupyter-widgets/base",
287 | "_view_module_version": "1.2.0",
288 | "_view_name": "StyleView",
289 | "bar_color": null,
290 | "description_width": ""
291 | }
292 | },
293 | "3401d2a346214667a76f63fc38286a93": {
294 | "model_module": "@jupyter-widgets/base",
295 | "model_name": "LayoutModel",
296 | "model_module_version": "1.2.0",
297 | "state": {
298 | "_model_module": "@jupyter-widgets/base",
299 | "_model_module_version": "1.2.0",
300 | "_model_name": "LayoutModel",
301 | "_view_count": null,
302 | "_view_module": "@jupyter-widgets/base",
303 | "_view_module_version": "1.2.0",
304 | "_view_name": "LayoutView",
305 | "align_content": null,
306 | "align_items": null,
307 | "align_self": null,
308 | "border": null,
309 | "bottom": null,
310 | "display": null,
311 | "flex": null,
312 | "flex_flow": null,
313 | "grid_area": null,
314 | "grid_auto_columns": null,
315 | "grid_auto_flow": null,
316 | "grid_auto_rows": null,
317 | "grid_column": null,
318 | "grid_gap": null,
319 | "grid_row": null,
320 | "grid_template_areas": null,
321 | "grid_template_columns": null,
322 | "grid_template_rows": null,
323 | "height": null,
324 | "justify_content": null,
325 | "justify_items": null,
326 | "left": null,
327 | "margin": null,
328 | "max_height": null,
329 | "max_width": null,
330 | "min_height": null,
331 | "min_width": null,
332 | "object_fit": null,
333 | "object_position": null,
334 | "order": null,
335 | "overflow": null,
336 | "overflow_x": null,
337 | "overflow_y": null,
338 | "padding": null,
339 | "right": null,
340 | "top": null,
341 | "visibility": null,
342 | "width": null
343 | }
344 | },
345 | "b0e130016a6a4ddfa52d25e01543d7b5": {
346 | "model_module": "@jupyter-widgets/controls",
347 | "model_name": "DescriptionStyleModel",
348 | "model_module_version": "1.5.0",
349 | "state": {
350 | "_model_module": "@jupyter-widgets/controls",
351 | "_model_module_version": "1.5.0",
352 | "_model_name": "DescriptionStyleModel",
353 | "_view_count": null,
354 | "_view_module": "@jupyter-widgets/base",
355 | "_view_module_version": "1.2.0",
356 | "_view_name": "StyleView",
357 | "description_width": ""
358 | }
359 | },
360 | "1fdec5cef57246c3a7b5f7495f2a7591": {
361 | "model_module": "@jupyter-widgets/controls",
362 | "model_name": "HBoxModel",
363 | "model_module_version": "1.5.0",
364 | "state": {
365 | "_dom_classes": [],
366 | "_model_module": "@jupyter-widgets/controls",
367 | "_model_module_version": "1.5.0",
368 | "_model_name": "HBoxModel",
369 | "_view_count": null,
370 | "_view_module": "@jupyter-widgets/controls",
371 | "_view_module_version": "1.5.0",
372 | "_view_name": "HBoxView",
373 | "box_style": "",
374 | "children": [
375 | "IPY_MODEL_cbb257d59a0040dc9c4c24c37f09bda0",
376 | "IPY_MODEL_ac73acc5fffc4ef081a7b92c7be19505",
377 | "IPY_MODEL_c002f141bd7a484b8ab242e967222d13"
378 | ],
379 | "layout": "IPY_MODEL_66e97f283a0148e88d20df545c6df990"
380 | }
381 | },
382 | "cbb257d59a0040dc9c4c24c37f09bda0": {
383 | "model_module": "@jupyter-widgets/controls",
384 | "model_name": "HTMLModel",
385 | "model_module_version": "1.5.0",
386 | "state": {
387 | "_dom_classes": [],
388 | "_model_module": "@jupyter-widgets/controls",
389 | "_model_module_version": "1.5.0",
390 | "_model_name": "HTMLModel",
391 | "_view_count": null,
392 | "_view_module": "@jupyter-widgets/controls",
393 | "_view_module_version": "1.5.0",
394 | "_view_name": "HTMLView",
395 | "description": "",
396 | "description_tooltip": null,
397 | "layout": "IPY_MODEL_b4f7aa47b37346d1b239cb45f0942445",
398 | "placeholder": "",
399 | "style": "IPY_MODEL_a80df20f06584e47839481c5e227dde8",
400 | "value": "100%"
401 | }
402 | },
403 | "ac73acc5fffc4ef081a7b92c7be19505": {
404 | "model_module": "@jupyter-widgets/controls",
405 | "model_name": "FloatProgressModel",
406 | "model_module_version": "1.5.0",
407 | "state": {
408 | "_dom_classes": [],
409 | "_model_module": "@jupyter-widgets/controls",
410 | "_model_module_version": "1.5.0",
411 | "_model_name": "FloatProgressModel",
412 | "_view_count": null,
413 | "_view_module": "@jupyter-widgets/controls",
414 | "_view_module_version": "1.5.0",
415 | "_view_name": "ProgressView",
416 | "bar_style": "success",
417 | "description": "",
418 | "description_tooltip": null,
419 | "layout": "IPY_MODEL_282cfd773a4147d185d26e37db8859cb",
420 | "max": 1000,
421 | "min": 0,
422 | "orientation": "horizontal",
423 | "style": "IPY_MODEL_241abbc0c6db473ea1f3f21fe355e8d6",
424 | "value": 1000
425 | }
426 | },
427 | "c002f141bd7a484b8ab242e967222d13": {
428 | "model_module": "@jupyter-widgets/controls",
429 | "model_name": "HTMLModel",
430 | "model_module_version": "1.5.0",
431 | "state": {
432 | "_dom_classes": [],
433 | "_model_module": "@jupyter-widgets/controls",
434 | "_model_module_version": "1.5.0",
435 | "_model_name": "HTMLModel",
436 | "_view_count": null,
437 | "_view_module": "@jupyter-widgets/controls",
438 | "_view_module_version": "1.5.0",
439 | "_view_name": "HTMLView",
440 | "description": "",
441 | "description_tooltip": null,
442 | "layout": "IPY_MODEL_5df417beb150409eab4cb686c468623d",
443 | "placeholder": "",
444 | "style": "IPY_MODEL_c7cde4c2286a4710b83aa088e4d1dfa1",
445 | "value": " 1000/1000 [00:02<00:00, 445.10it/s]"
446 | }
447 | },
448 | "66e97f283a0148e88d20df545c6df990": {
449 | "model_module": "@jupyter-widgets/base",
450 | "model_name": "LayoutModel",
451 | "model_module_version": "1.2.0",
452 | "state": {
453 | "_model_module": "@jupyter-widgets/base",
454 | "_model_module_version": "1.2.0",
455 | "_model_name": "LayoutModel",
456 | "_view_count": null,
457 | "_view_module": "@jupyter-widgets/base",
458 | "_view_module_version": "1.2.0",
459 | "_view_name": "LayoutView",
460 | "align_content": null,
461 | "align_items": null,
462 | "align_self": null,
463 | "border": null,
464 | "bottom": null,
465 | "display": null,
466 | "flex": null,
467 | "flex_flow": null,
468 | "grid_area": null,
469 | "grid_auto_columns": null,
470 | "grid_auto_flow": null,
471 | "grid_auto_rows": null,
472 | "grid_column": null,
473 | "grid_gap": null,
474 | "grid_row": null,
475 | "grid_template_areas": null,
476 | "grid_template_columns": null,
477 | "grid_template_rows": null,
478 | "height": null,
479 | "justify_content": null,
480 | "justify_items": null,
481 | "left": null,
482 | "margin": null,
483 | "max_height": null,
484 | "max_width": null,
485 | "min_height": null,
486 | "min_width": null,
487 | "object_fit": null,
488 | "object_position": null,
489 | "order": null,
490 | "overflow": null,
491 | "overflow_x": null,
492 | "overflow_y": null,
493 | "padding": null,
494 | "right": null,
495 | "top": null,
496 | "visibility": null,
497 | "width": null
498 | }
499 | },
500 | "b4f7aa47b37346d1b239cb45f0942445": {
501 | "model_module": "@jupyter-widgets/base",
502 | "model_name": "LayoutModel",
503 | "model_module_version": "1.2.0",
504 | "state": {
505 | "_model_module": "@jupyter-widgets/base",
506 | "_model_module_version": "1.2.0",
507 | "_model_name": "LayoutModel",
508 | "_view_count": null,
509 | "_view_module": "@jupyter-widgets/base",
510 | "_view_module_version": "1.2.0",
511 | "_view_name": "LayoutView",
512 | "align_content": null,
513 | "align_items": null,
514 | "align_self": null,
515 | "border": null,
516 | "bottom": null,
517 | "display": null,
518 | "flex": null,
519 | "flex_flow": null,
520 | "grid_area": null,
521 | "grid_auto_columns": null,
522 | "grid_auto_flow": null,
523 | "grid_auto_rows": null,
524 | "grid_column": null,
525 | "grid_gap": null,
526 | "grid_row": null,
527 | "grid_template_areas": null,
528 | "grid_template_columns": null,
529 | "grid_template_rows": null,
530 | "height": null,
531 | "justify_content": null,
532 | "justify_items": null,
533 | "left": null,
534 | "margin": null,
535 | "max_height": null,
536 | "max_width": null,
537 | "min_height": null,
538 | "min_width": null,
539 | "object_fit": null,
540 | "object_position": null,
541 | "order": null,
542 | "overflow": null,
543 | "overflow_x": null,
544 | "overflow_y": null,
545 | "padding": null,
546 | "right": null,
547 | "top": null,
548 | "visibility": null,
549 | "width": null
550 | }
551 | },
552 | "a80df20f06584e47839481c5e227dde8": {
553 | "model_module": "@jupyter-widgets/controls",
554 | "model_name": "DescriptionStyleModel",
555 | "model_module_version": "1.5.0",
556 | "state": {
557 | "_model_module": "@jupyter-widgets/controls",
558 | "_model_module_version": "1.5.0",
559 | "_model_name": "DescriptionStyleModel",
560 | "_view_count": null,
561 | "_view_module": "@jupyter-widgets/base",
562 | "_view_module_version": "1.2.0",
563 | "_view_name": "StyleView",
564 | "description_width": ""
565 | }
566 | },
567 | "282cfd773a4147d185d26e37db8859cb": {
568 | "model_module": "@jupyter-widgets/base",
569 | "model_name": "LayoutModel",
570 | "model_module_version": "1.2.0",
571 | "state": {
572 | "_model_module": "@jupyter-widgets/base",
573 | "_model_module_version": "1.2.0",
574 | "_model_name": "LayoutModel",
575 | "_view_count": null,
576 | "_view_module": "@jupyter-widgets/base",
577 | "_view_module_version": "1.2.0",
578 | "_view_name": "LayoutView",
579 | "align_content": null,
580 | "align_items": null,
581 | "align_self": null,
582 | "border": null,
583 | "bottom": null,
584 | "display": null,
585 | "flex": null,
586 | "flex_flow": null,
587 | "grid_area": null,
588 | "grid_auto_columns": null,
589 | "grid_auto_flow": null,
590 | "grid_auto_rows": null,
591 | "grid_column": null,
592 | "grid_gap": null,
593 | "grid_row": null,
594 | "grid_template_areas": null,
595 | "grid_template_columns": null,
596 | "grid_template_rows": null,
597 | "height": null,
598 | "justify_content": null,
599 | "justify_items": null,
600 | "left": null,
601 | "margin": null,
602 | "max_height": null,
603 | "max_width": null,
604 | "min_height": null,
605 | "min_width": null,
606 | "object_fit": null,
607 | "object_position": null,
608 | "order": null,
609 | "overflow": null,
610 | "overflow_x": null,
611 | "overflow_y": null,
612 | "padding": null,
613 | "right": null,
614 | "top": null,
615 | "visibility": null,
616 | "width": null
617 | }
618 | },
619 | "241abbc0c6db473ea1f3f21fe355e8d6": {
620 | "model_module": "@jupyter-widgets/controls",
621 | "model_name": "ProgressStyleModel",
622 | "model_module_version": "1.5.0",
623 | "state": {
624 | "_model_module": "@jupyter-widgets/controls",
625 | "_model_module_version": "1.5.0",
626 | "_model_name": "ProgressStyleModel",
627 | "_view_count": null,
628 | "_view_module": "@jupyter-widgets/base",
629 | "_view_module_version": "1.2.0",
630 | "_view_name": "StyleView",
631 | "bar_color": null,
632 | "description_width": ""
633 | }
634 | },
635 | "5df417beb150409eab4cb686c468623d": {
636 | "model_module": "@jupyter-widgets/base",
637 | "model_name": "LayoutModel",
638 | "model_module_version": "1.2.0",
639 | "state": {
640 | "_model_module": "@jupyter-widgets/base",
641 | "_model_module_version": "1.2.0",
642 | "_model_name": "LayoutModel",
643 | "_view_count": null,
644 | "_view_module": "@jupyter-widgets/base",
645 | "_view_module_version": "1.2.0",
646 | "_view_name": "LayoutView",
647 | "align_content": null,
648 | "align_items": null,
649 | "align_self": null,
650 | "border": null,
651 | "bottom": null,
652 | "display": null,
653 | "flex": null,
654 | "flex_flow": null,
655 | "grid_area": null,
656 | "grid_auto_columns": null,
657 | "grid_auto_flow": null,
658 | "grid_auto_rows": null,
659 | "grid_column": null,
660 | "grid_gap": null,
661 | "grid_row": null,
662 | "grid_template_areas": null,
663 | "grid_template_columns": null,
664 | "grid_template_rows": null,
665 | "height": null,
666 | "justify_content": null,
667 | "justify_items": null,
668 | "left": null,
669 | "margin": null,
670 | "max_height": null,
671 | "max_width": null,
672 | "min_height": null,
673 | "min_width": null,
674 | "object_fit": null,
675 | "object_position": null,
676 | "order": null,
677 | "overflow": null,
678 | "overflow_x": null,
679 | "overflow_y": null,
680 | "padding": null,
681 | "right": null,
682 | "top": null,
683 | "visibility": null,
684 | "width": null
685 | }
686 | },
687 | "c7cde4c2286a4710b83aa088e4d1dfa1": {
688 | "model_module": "@jupyter-widgets/controls",
689 | "model_name": "DescriptionStyleModel",
690 | "model_module_version": "1.5.0",
691 | "state": {
692 | "_model_module": "@jupyter-widgets/controls",
693 | "_model_module_version": "1.5.0",
694 | "_model_name": "DescriptionStyleModel",
695 | "_view_count": null,
696 | "_view_module": "@jupyter-widgets/base",
697 | "_view_module_version": "1.2.0",
698 | "_view_name": "StyleView",
699 | "description_width": ""
700 | }
701 | }
702 | }
703 | },
704 | "accelerator": "GPU"
705 | },
706 | "cells": [
707 | {
708 | "cell_type": "code",
709 | "source": [
710 | "# !!!! Choose Runtime > Change Runtime Type and set Hardware Accelerator to GPU !!!!"
711 | ],
712 | "metadata": {
713 | "id": "xoISxNMeKd3f"
714 | },
715 | "execution_count": 1,
716 | "outputs": []
717 | },
718 | {
719 | "cell_type": "code",
720 | "execution_count": 2,
721 | "metadata": {
722 | "colab": {
723 | "base_uri": "https://localhost:8080/"
724 | },
725 | "id": "5lvt7IS0CyCY",
726 | "outputId": "10396953-c4f0-4e4b-d89a-2e27c28c7249"
727 | },
728 | "outputs": [
729 | {
730 | "output_type": "stream",
731 | "name": "stdout",
732 | "text": [
733 | "Using pip 22.0.4 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8)\n",
734 | "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
735 | "Collecting git+https://github.com/tatsushi-ikeda/pyheom.git@master\n",
736 | " Cloning https://github.com/tatsushi-ikeda/pyheom.git (to revision master) to /tmp/pip-req-build-wp6adq8v\n",
737 | " Running command git version\n",
738 | " git version 2.25.1\n",
739 | " Running command git clone --filter=blob:none https://github.com/tatsushi-ikeda/pyheom.git /tmp/pip-req-build-wp6adq8v\n",
740 | " Cloning into '/tmp/pip-req-build-wp6adq8v'...\n",
741 | " Running command git show-ref master\n",
742 | " 9b084f3d8dc4edffb97d8d961bd5038f3aead919 refs/heads/master\n",
743 | " 9b084f3d8dc4edffb97d8d961bd5038f3aead919 refs/remotes/origin/master\n",
744 | " Running command git symbolic-ref -q HEAD\n",
745 | " refs/heads/master\n",
746 | " Resolved https://github.com/tatsushi-ikeda/pyheom.git to commit 9b084f3d8dc4edffb97d8d961bd5038f3aead919\n",
747 | " Running command git submodule update --init --recursive -q\n",
748 | " Running command python setup.py egg_info\n",
749 | " running egg_info\n",
750 | " creating /tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info\n",
751 | " writing /tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/PKG-INFO\n",
752 | " writing dependency_links to /tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/dependency_links.txt\n",
753 | " writing requirements to /tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/requires.txt\n",
754 | " writing top-level names to /tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/top_level.txt\n",
755 | " writing manifest file '/tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/SOURCES.txt'\n",
756 | " adding license file 'LICENSE.txt'\n",
757 | " writing manifest file '/tmp/pip-pip-egg-info-hrnv0uoo/pyheom.egg-info/SOURCES.txt'\n",
758 | " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
759 | "Requirement already satisfied: numpy in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (1.21.6)\n",
760 | "Requirement already satisfied: scipy in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (1.7.3)\n",
761 | "Requirement already satisfied: jinja2 in /usr/local/lib/python3.8/dist-packages (from pyheom==1.0.0a2) (2.11.3)\n",
762 | "Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.8/dist-packages (from jinja2->pyheom==1.0.0a2) (2.0.1)\n"
763 | ]
764 | }
765 | ],
766 | "source": [
767 | "! CMAKE_ARGS=\"-DLIBHEOM_ENABLE_CUDA=ON -DLIBHEOM_ENABLE_EIGEN=OFF -DCMAKE_VERBOSE_MAKEFILE=ON\" pip install git+https://github.com/tatsushi-ikeda/pyheom.git@master -v\n",
768 | "# Unfortunately, enabling both CUDA and EIGEN causes compile errors in the Colab environment."
769 | ]
770 | },
771 | {
772 | "cell_type": "code",
773 | "source": [
774 | "import numpy as np\n",
775 | "import scipy as sp\n",
776 | "import scipy.sparse\n",
777 | "\n",
778 | "from sys import stdout, stderr\n",
779 | "import time\n",
780 | "\n",
781 | "import pyheom\n",
782 | "from pyheom import heom_solver, redfield_solver, noise_decomposition, drude, unit\n",
783 | "pyheom.units['energy'] = unit.wavenumber\n",
784 | "pyheom.units['time'] = unit.femtosecond\n",
785 | "from tqdm.auto import tqdm\n",
786 | "\n",
787 | "dtype = np.complex128\n",
788 | "\n",
789 | "epsilon_1 = 100.0\n",
790 | "epsilon_2 = 0.0\n",
791 | "J_12 = 100.0\n",
792 | "gamma = 53.08\n",
793 | "beta = 1/208.51\n",
794 | "lambda_c = J_12/5.0\n",
795 | "\n",
796 | "callback_interval = 1\n",
797 | "count = 1000\n",
798 | "t_list = np.arange(0, count, callback_interval)\n",
799 | "solver_params = dict(\n",
800 | " dt = 5e-2,\n",
801 | " # atol=1e-6, rtol=1e-3\n",
802 | ")\n",
803 | "# \n",
804 | "\n",
805 | "J_1 = drude(eta = 2*lambda_c/gamma, gamma_c = gamma)\n",
806 | "J_2 = drude(eta = 2*lambda_c/gamma, gamma_c = gamma)\n",
807 | "corr_dict_1 = noise_decomposition(\n",
808 | " J_1,\n",
809 | " T = 1/beta,\n",
810 | " type_ltc = 'none'\n",
811 | ")\n",
812 | "corr_dict_2 = noise_decomposition(\n",
813 | " J_2,\n",
814 | " T = 1/beta,\n",
815 | " type_ltc = 'none'\n",
816 | ")\n",
817 | "\n",
818 | "n_level = 2\n",
819 | "depth = 20\n",
820 | "\n",
821 | "H = np.array([[epsilon_1+lambda_c, J_12],\n",
822 | " [J_12, epsilon_2+lambda_c]],\n",
823 | " dtype=dtype)\n",
824 | "\n",
825 | "V_1 = np.array([[1, 0],\n",
826 | " [0, 0]],\n",
827 | " dtype=dtype)\n",
828 | "\n",
829 | "V_2 = np.array([[0, 0],\n",
830 | " [0, 1]],\n",
831 | " dtype=dtype)\n"
832 | ],
833 | "metadata": {
834 | "id": "g8NYq_4a3Cqn"
835 | },
836 | "execution_count": 3,
837 | "outputs": []
838 | },
839 | {
840 | "cell_type": "code",
841 | "source": [
842 | "print(corr_dict_1)"
843 | ],
844 | "metadata": {
845 | "colab": {
846 | "base_uri": "https://localhost:8080/"
847 | },
848 | "id": "CO4pXfaP_2sT",
849 | "outputId": "04be0ff5-8fa7-4c82-cc42-5b9b854b65cc"
850 | },
851 | "execution_count": 4,
852 | "outputs": [
853 | {
854 | "output_type": "stream",
855 | "name": "stdout",
856 | "text": [
857 | "{'gamma': <1x1 sparse matrix of type ''\n",
858 | "\twith 1 stored elements in List of Lists format>, 'sigma': array([1.+0.j]), 'phi_0': array([1.+0.j]), 'S': <1x1 sparse matrix of type ''\n",
859 | "\twith 1 stored elements in List of Lists format>, 's_delta': 0.0, 'A': <1x1 sparse matrix of type ''\n",
860 | "\twith 1 stored elements in List of Lists format>}\n"
861 | ]
862 | }
863 | ]
864 | },
865 | {
866 | "cell_type": "code",
867 | "source": [
868 | "# Symmetrized correlation function : sigma.T@S@exp(-gamma*t)@phi_0 + s_delta*2*delta(t)\n",
869 | "# Anti-symmetrized correlation function : sigma.T@A@exp(-gamma*t)@phi_0\n",
870 | "# If you want to use custom correlation functions, specify the above parameters directly.\n",
871 | "# Because gamma, S, and A are matrices and phi_0 and sigma are vectors, sum of exponential functions (diagonal gamma) and non-exponential funcitons (non-diagonal gamma) can be written in the above format."
872 | ],
873 | "metadata": {
874 | "id": "Al50Ww9r9Yx5"
875 | },
876 | "execution_count": 5,
877 | "outputs": []
878 | },
879 | {
880 | "cell_type": "code",
881 | "source": [
882 | "space = 'ado' # 'hilbert', 'liouville'\n",
883 | "format = 'sparse' # 'dense'\n",
884 | "engine = 'cuda' # 'eigen', 'mkl'\n",
885 | "solver = 'lsrk4'\n",
886 | "order_liouville = 'row_major' # 'col_major'\n",
887 | "# The above configurations strongly affect the numerical performance of the computation.\n",
888 | "# The optimal choice depends on the sizes of the system and hierarchy space.\n",
889 | "\n",
890 | "qme = heom_solver(H, [dict(V=V_1, **corr_dict_1), dict(V=V_2, **corr_dict_2)],\n",
891 | " space=space, format=format, engine=engine,\n",
892 | " order_liouville=order_liouville,\n",
893 | " solver=solver,\n",
894 | " engine_args=dict(),\n",
895 | " depth = depth)\n",
896 | "\n",
897 | "n_storage = qme.storage_size()\n",
898 | "\n",
899 | "rho = np.zeros((n_storage,n_level,n_level), dtype=dtype)\n",
900 | "rho_0 = rho[0,:,:]\n",
901 | "rho_0[0,0] = 1\n",
902 | "\n",
903 | "pop_save_heom = np.zeros((t_list.shape[0],))\n",
904 | "count = 0\n",
905 | "\n",
906 | "with tqdm(total=t_list.shape[0]) as bar:\n",
907 | " def callback(t):\n",
908 | " global count\n",
909 | " bar.update(1)\n",
910 | " pop_save_heom[count] = rho_0[0,0].real\n",
911 | " count += 1\n",
912 | " begin = time.time()\n",
913 | " qme.solve(rho, t_list, callback=callback, **solver_params)\n",
914 | " end = time.time()\n",
915 | "print('elapsed:', end - begin, file=stderr)\n",
916 | "del qme"
917 | ],
918 | "metadata": {
919 | "id": "mk70vMXqEAvm",
920 | "colab": {
921 | "base_uri": "https://localhost:8080/",
922 | "height": 67,
923 | "referenced_widgets": [
924 | "a7348b593dc9492ba39ddc070f8309cc",
925 | "82aaefaf9e5649e5be9f0beef6e9af55",
926 | "ecf40304e63240dab9bb9d013cb27916",
927 | "19808be485524fb48d4576dc1f26e320",
928 | "71b0e54590204030acd0247529e3014a",
929 | "4e676805dfc34235b8d40a9a3fe1ecbb",
930 | "cc0f24a6e2204fe2941cfd8215838e2a",
931 | "7315beb3a1274fe3b3013bd5bc436c4d",
932 | "02f2a5e6658d4cbba01176fbd0bb95cf",
933 | "3401d2a346214667a76f63fc38286a93",
934 | "b0e130016a6a4ddfa52d25e01543d7b5"
935 | ]
936 | },
937 | "outputId": "5d11dcfb-dd66-4437-e8d4-d09eb937c157"
938 | },
939 | "execution_count": 6,
940 | "outputs": [
941 | {
942 | "output_type": "display_data",
943 | "data": {
944 | "text/plain": [
945 | " 0%| | 0/1000 [00:00, ?it/s]"
946 | ],
947 | "application/vnd.jupyter.widget-view+json": {
948 | "version_major": 2,
949 | "version_minor": 0,
950 | "model_id": "a7348b593dc9492ba39ddc070f8309cc"
951 | }
952 | },
953 | "metadata": {}
954 | },
955 | {
956 | "output_type": "stream",
957 | "name": "stderr",
958 | "text": [
959 | "elapsed: 2.529841899871826\n"
960 | ]
961 | }
962 | ]
963 | },
964 | {
965 | "cell_type": "code",
966 | "source": [
967 | "# In this example, calculations with a GPU device is not efficient because both system size and hierarchy are too small. "
968 | ],
969 | "metadata": {
970 | "id": "BtRJcnSUK_Jv"
971 | },
972 | "execution_count": 7,
973 | "outputs": []
974 | },
975 | {
976 | "cell_type": "code",
977 | "source": [
978 | "space='liouville'\n",
979 | "qme = redfield_solver(H, [dict(V=V_1, **corr_dict_1), dict(V=V_2, **corr_dict_2)],\n",
980 | " space=space, format=format, engine=engine,\n",
981 | " order_liouville=order_liouville,\n",
982 | " solver=solver,\n",
983 | " engine_args=dict())\n",
984 | "\n",
985 | "n_storage = qme.storage_size()\n",
986 | "\n",
987 | "rho = np.zeros((n_storage,n_level,n_level), dtype=dtype)\n",
988 | "rho_0 = rho[0,:,:]\n",
989 | "rho_0[0,0] = 1\n",
990 | "\n",
991 | "pop_save_rf = np.zeros((t_list.shape[0],))\n",
992 | "count = 0\n",
993 | "\n",
994 | "with tqdm(total=t_list.shape[0]) as bar:\n",
995 | " def callback(t):\n",
996 | " global count\n",
997 | " bar.update(1)\n",
998 | " pop_save_rf[count] = rho_0[0,0].real\n",
999 | " count += 1\n",
1000 | " begin = time.time()\n",
1001 | " qme.solve(rho, t_list, callback=callback, **solver_params)\n",
1002 | " end = time.time()\n",
1003 | "print('elapsed:', end - begin, file=stderr)\n",
1004 | "del qme"
1005 | ],
1006 | "metadata": {
1007 | "colab": {
1008 | "base_uri": "https://localhost:8080/",
1009 | "height": 67,
1010 | "referenced_widgets": [
1011 | "1fdec5cef57246c3a7b5f7495f2a7591",
1012 | "cbb257d59a0040dc9c4c24c37f09bda0",
1013 | "ac73acc5fffc4ef081a7b92c7be19505",
1014 | "c002f141bd7a484b8ab242e967222d13",
1015 | "66e97f283a0148e88d20df545c6df990",
1016 | "b4f7aa47b37346d1b239cb45f0942445",
1017 | "a80df20f06584e47839481c5e227dde8",
1018 | "282cfd773a4147d185d26e37db8859cb",
1019 | "241abbc0c6db473ea1f3f21fe355e8d6",
1020 | "5df417beb150409eab4cb686c468623d",
1021 | "c7cde4c2286a4710b83aa088e4d1dfa1"
1022 | ]
1023 | },
1024 | "id": "EkctGnOCvjvR",
1025 | "outputId": "60fd4705-a092-4b78-a375-0ad8ecbae469"
1026 | },
1027 | "execution_count": 8,
1028 | "outputs": [
1029 | {
1030 | "output_type": "display_data",
1031 | "data": {
1032 | "text/plain": [
1033 | " 0%| | 0/1000 [00:00, ?it/s]"
1034 | ],
1035 | "application/vnd.jupyter.widget-view+json": {
1036 | "version_major": 2,
1037 | "version_minor": 0,
1038 | "model_id": "1fdec5cef57246c3a7b5f7495f2a7591"
1039 | }
1040 | },
1041 | "metadata": {}
1042 | },
1043 | {
1044 | "output_type": "stream",
1045 | "name": "stderr",
1046 | "text": [
1047 | "elapsed: 2.1740167140960693\n"
1048 | ]
1049 | }
1050 | ]
1051 | },
1052 | {
1053 | "cell_type": "code",
1054 | "source": [
1055 | "import matplotlib.pyplot as plt\n",
1056 | "fig = plt.figure(figsize=(6, 4))\n",
1057 | "ax = fig.add_subplot(111, \n",
1058 | " xlabel='time / fs', \n",
1059 | " ylabel='population')\n",
1060 | "ax.plot(t_list, pop_save_heom)\n",
1061 | "ax.plot(t_list, pop_save_rf)"
1062 | ],
1063 | "metadata": {
1064 | "colab": {
1065 | "base_uri": "https://localhost:8080/",
1066 | "height": 297
1067 | },
1068 | "id": "oQCDwnSmJvqD",
1069 | "outputId": "4b14a29a-6408-48d9-9393-edfcba760f89"
1070 | },
1071 | "execution_count": 9,
1072 | "outputs": [
1073 | {
1074 | "output_type": "execute_result",
1075 | "data": {
1076 | "text/plain": [
1077 | "[]"
1078 | ]
1079 | },
1080 | "metadata": {},
1081 | "execution_count": 9
1082 | },
1083 | {
1084 | "output_type": "display_data",
1085 | "data": {
1086 | "text/plain": [
1087 | ""
1088 | ],
1089 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYIAAAEGCAYAAABo25JHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3dd5hcZdn48e89M9v7bN/NJrvpjRTSqIYiHUFFkSKigOgrxf5aX1TUn/VVUdFXxAYqKIgQIBKpAoGQQuqmbjZld5Ns7312nt8f52zYbLbMzs7Z2ezcn+s61845c86ZZzIw9zztfsQYg1JKqcjlCncBlFJKhZcGAqWUinAaCJRSKsJpIFBKqQingUAppSKcJ9wFGKmMjAxTWFgY7mIopdQpZdOmTTXGmMyBnjvlAkFhYSEbN24MdzGUUuqUIiKHBntOm4aUUirCaSBQSqkIp4FAKaUinAYCpZSKcBoIlFIqwjkWCETk9yJSJSI7BnleROTnIlIiIttE5HSnyqKUUmpwTtYI/ghcOsTzlwEz7O124NcOlkUppdQgHAsExphXgbohTrkaeMhY1gGpIpLrVHn2bFnLW3/8Eq2tLU69hFJKnZLC2UeQD5T12S+3j51ERG4XkY0isrG6ujqoF6vbvoYVB/+Ptx78TFDXK6XURHVKdBYbYx4wxiw1xizNzBxwhvSwzrzpXnamX8ySumfZfyS4YKKUUhNROANBBVDQZ3+SfcwxeefdQoq0Ubz2GSdfRimlTinhDASrgI/Yo4fOABqNMUedfMHUWSvx4aaz9A0nX0YppU4pjiWdE5FHgPOADBEpB74BRAEYY/4PWA1cDpQAbcDHnCrLcdHxVCfOZnLTNtq6fMRHn3I595RSKuQc+yY0xlw/zPMGuMOp1x+ML2cxc5v/wfayBlZMyxjrl1dKqXHnlOgsDqW0wgUkSTslJbvDXRSllBoXIi4QJBYsAKCtfMAJz0opFXEiLhCQNRsAT+2eMBdEKaXGh8gLBHFptLmTiWstw+qmUEqpyBZ5gQBoT5hErr+So40d4S6KUkqFXUQGAtIKKZAq9lVp3iGllIrIQBCXNZV8qaG0qincRVFKqbCL2EAQIz4aq8qGP1kppSa4iAwEkjYFgO7q0jCXRCmlwi8iAwGphQC4Gg+HtxxKKTUORGYgSLGWPYhqPapDSJVSES8yA0FUHB2eZFJ7amlq94W7NEopFVaRGQiA7vhscqSOsvq2cBdFKaXCKmIDAcl5ZEs95RoIlFIRLmIDgSc1nxypo7KpM9xFUUqpsIrYlVli0/KIppHKRp1drJSKbBFbI5DkPNxiaK9zdHVMpZQa9yI2EJCcB4CvsSLMBVFKqfCK3ECQlAuANFeGuSBKKRVekRsIErMAcLfVhLkgSikVXo4GAhG5VET2iEiJiHx5gOeniMiLIrJNRF4RkUlOlucE8dbC9Ym+Olo7dVKZUipyORYIRMQN3A9cBswFrheRuf1O+zHwkDFmAXAv8D2nynMSTzRdUclkSCOVTbpAjVIqcjlZI1gOlBhjSo0xXcCjwNX9zpkLvGQ/fnmA5x3li8sgXZp0LoFSKqI5GQjygb4J/8vtY31tBd5vP34fkCQi6f1vJCK3i8hGEdlYXV0dsgJKYhaZ0khVs9YIlFKRK9ydxV8AVorIZmAlUAH09D/JGPOAMWapMWZpZmZmyF7ck5xNOk0c07WLlVIRzMmZxRVAQZ/9Sfax44wxR7BrBCKSCFxjjGlwsEwniErqrRFo05BSKnI5WSPYAMwQkSIRiQauA1b1PUFEMkSktwxfAX7vYHlOlphFirTS0KxpJpRSkcuxQGCM8QF3AmuAXcDfjTHFInKviFxln3YesEdE9gLZwHedKs+AEqwhpN1Noet3UEqpU42jSeeMMauB1f2O3dPn8ePA406WYUgJ1qQyf4sGAqVU5Ap3Z3F4JVgdz642DQRKqcgV2YEg0QoE0Z21+P26drFSKjJFdiCwawRe00BTR3eYC6OUUuER2YEgOhGfO5Z0aaKmpSvcpVFKqbCI7EAgQk9MKmm0UNOicwmUUpEpsgMBYOLSSZNmarVGoJSKUBEfCFwJXtKkhdpWrREopSJTxAcCT2IGXpq1j0ApFbEiPhC4EtLxulqo1T4CpVSEivhAQJyXJFqpa24Pd0mUUiosNBDEp+PGT0dLXbhLopRSYaGBIN4LQE9LbZgLopRS4aGBwA4Epk0DgVIqMmkgiLMCQXRXA52+kxZHU0qpCU8DQby1RLJXmmlo03xDSqnIo4HAbhpKpYX6Np1LoJSKPBoIohPxu6LwSjN1rRoIlFKRRwOBCD2xaaTSok1DSqmIpIEAIM6LV5q1aUgpFZEcDQQicqmI7BGREhH58gDPTxaRl0Vks4hsE5HLnSzPYNyJGaRKC/XaNKSUikCOBQIRcQP3A5cBc4HrRWRuv9O+DvzdGLMYuA74lVPlGYor3kuGNFOvTUNKqQjkZI1gOVBijCk1xnQBjwJX9zvHAMn24xTgiIPlGVx8Ommio4aUUpHJ4+C984GyPvvlwIp+53wT+LeI3AUkAO92sDyDi/eSQjP1moFUKRWBwt1ZfD3wR2PMJOBy4GEROalMInK7iGwUkY3V1dWhL4WdeK6rtSH091ZKqXHOyUBQART02Z9kH+vrVuDvAMaYN4FYIKP/jYwxDxhjlhpjlmZmZoa+pHaaCTTfkFIqAjkZCDYAM0SkSESisTqDV/U75zBwIYCIzMEKBA785B+GPbuYdk1FrZSKPI4FAmOMD7gTWAPswhodVCwi94rIVfZpnwc+LiJbgUeAjxpjjFNlGpSdbyi6qwFfj3/MX14ppcLJyc5ijDGrgdX9jt3T5/FO4GwnyxCQuDQA0mimob2bjMSYMBdIKaXGTrg7i8cHu2koTVpo0CGkSqkIo4EAICYFIy5SpEUnlSmlIo4GAgCXi57oFNJo0QykSqmIo4HAZuK82jSklIpIGghsrgQvKWjTkFIq8mggsLnivXhdmoFUKRV5NBDYJN6LVxPPKaUikAaCXnFee91ibRpSSkUWDQS94tKIo4PmlpZwl0QppcaUBoJe8dbs4p5WzTeklIosGgh62RlIpb0+zAVRSqmxpYGgl51mwtXZQDjy3imlVLgEnHTOXoM4u+81xpjDThQqLOzEcymmmaYOHylxUWEukFJKjY2AAoG9lOQ3gEqgN0+zARY4VK6xZzcNpdqzizUQKKUiRaA1gk8Ds4wxE3cJL7tpqHcI6ZT0MJdHKaXGSKB9BGVAo5MFCbuoePyuaM03pJSKOIHWCEqBV0TkWaCz96Ax5ieOlCocRPDHppHa1UyDTipTSkWQQAPBYXuLtreJKd5LWnMLR7RGoJSKIAEFAmPMtwBEJNHen5DTb93xaaRKHcVhrhF0+np4+1AD07ISyEqKDWtZlFITX6CjhuYDDwNee78G+IgxptjBso05ifeS7ioLax9BU0c31z+wjuIjTcRGuXjwI8s4Z0ZG2MqjlJr4Au0sfgD4nDFmijFmCvB54LfDXSQil4rIHhEpEZEvD/D8T0Vki73tFZGGkRU/xOJ7F6cJX43ge6t3s+toE9++eh5TvAnc/ehmGtu1z0Ip5ZxAA0GCMebl3h1jzCtAwlAX2BPQ7gcuA+YC14vI3L7nGGM+a4xZZIxZBPwCeGIEZQ+9uDSSTTP1rZ3Dn+uAyqYOHt9UxofPmMJNZxbyv9cupK61iz+sPRCW8iilIkOggaBURP5HRArt7etYI4mGshwoMcaUGmO6gEeBq4c4/3rgkQDL44w4L1H46GhrDsvL/3ndIXx+w23nTAVgfn4K75qZyaPry/D1+Ie5WimlghNoILgFyMT6xf6E/fiWYa7Jx5p/0KvcPnYSEZkCFAEvDfL87SKyUUQ2VldXB1jkINiTyvytYz9vzhjD01uPcM70DCanxx8/fuOKyRxr6uCVPQ6+b6VURAsoEBhj6o0xdxtjTre3TxtjQpmm8zrgcWNMzyCv/4AxZqkxZmlmZmYIX7af3gykHWPfVVFS1cLB2jYunpdzwvELZmeRFOthTfGxMS+TUioyDDlqSER+Zoz5jIg8jZVb6ATGmKuGuLwCKOizP8k+NpDrgDuGKavz7MRzMd0NdPf4iXKPXXLWf++sBOCiOdknHI9yuzh/VhYv7a6ix29wu2TMyqSUigzDDR992P774yDuvQGYISJFWAHgOuCG/ieJyGwgDXgziNcILbtpKA1r5FBmUsyYvfS60lpm5ySRk3LyvIGL5mazausRtpTVs2SKd8zKpJSKDEP+5DXGbLIfLjLG/KfvBiwa5lofcCewBtgF/N0YUywi94pI35rEdcCjZjwsAtAvA+lY6e7xs+lQPSuKBv6SP3u6NY9gXamunqaUCr1A2z5uHuDYR4e7yBiz2hgz0xgzzRjzXfvYPcaYVX3O+aYx5qQ5BmFhNw2N9SL2xUeaaOvqYXnRwClPvQnRzMxO5K0DGgiUUqE3XB/B9VjNOUUisqrPU0nAxPtW8kTTE5VAmq+F+jGsEWw8aP1TLitMG/ScFUXpPPF2Ob4eP54x7LtQSk18w/URvAEcBTKA/+1zvBnY5lShwsnEppHa0TymTUPbKxrJTYklK3nwvELLi7w8vO4QxUeaWFiQOmZlU0pNfEMGAmPMIeAQcObYFCf8JN5LakMrJWPcNDQvL2XIc3r7D9YfqNNAoJQKqYDaGETkDBHZICItItIlIj0i0uR04cLBleDFK2PXR9DW5WN/dQvz8pKHPC8rOZb81Di2loc3HZNSauIJtLH5l1gpIPYBccBtWHmEJhyJ8+J1jd2ooV1HmzDGSicxnAWTUthRMbEXilNKjb2Aex2NMSWA2xjTY4z5A3Cpc8UKo3gvqTJ2ncXFR6yK1XA1ArCCxcHaNhp1BTWlVAgFGgjaRCQa2CIiPxSRz47g2lNLXBpJppWGMcpAuqOiEW9CNLkDTCTrb8Ekq9aw44jWCpRSoRPol/lNgBtrglgrVuqIa5wqVFjFeXHhp6d1bNri91S2MCs7CZHhU0ecZjcfbSvXQKCUCp1Al6o8ZD9sB77lXHHGATvNhGl3PgOpMYb9VS28b/GASVlPkhofzWRvPNsrtMNYKRU6w00o284AyeZ6GWMWhLxE4WbPLpaOBowxAf1SD1ZlUyctnT5mZCcGfM3c3GR2Hw3PeglKqYlpuBrBlWNSivHEzjeU6LfSPiTEBFRpCkpJVQsA0zMDDwSzcpL4985jtHf1EBftdqpoSqkIEsiEssjSJwNpfVuXw4HA+mU/PSvwQDA7Jwm/gX1VzSyYpBPLlFKjF+iEsmYRabK3jok8oex44rkxWMS+pLqFpFjPiNJdz8pJAmD3MW0eUkqFRqCdxUm9j8VqNL8aOMOpQoVVbAoGGZtAUNXC9KzEEfVDTElPIDbKxR4NBEqpEBnxXABjeRK4xIHyhJ/LjT8m5XjTkJNKq1tH1D8A4HYJM7OTNBAopUImoBqBiLy/z64LWAp0OFKiccDEe0ltczbNRFuXj6rmTgozEkZ87azsJF7eU+VAqZRSkSjQntD39HnsAw5iNQ9NSK54L6m0cNDBpqFDtW0ATEmPH/G1s3KSeGxTOTUtnWQkjt1ymkqpiSnQPoKPOV2Q8cQV7yXdtc/RpqHeQFCYPvIawewcKy/R3mPNZEzXQKCUGp1ARw1NFZGnRaRaRKpE5CkRmep04cImzkpF7WRyt0O1rQBMDqJG0DvctKS6JaRlUkpFpkA7i/8K/B3IBfKAx4BHnCpU2MWlkeJwBtJDdW2kxUeRHBs14muzk2NIjPGwv0oDgVJq9AINBPHGmIeNMT57+zMwbLpMEblURPaISImIDLhAvYhcKyI7RaRYRP46ksI7Jt5LvGmnubXNsZc4VNvKlCCahQBEhGmZCVojUEqFRKCdxf+yv8gfxco99CFgtYh4AYwxJy1kLyJurMVrLgLKgQ0issoYs7PPOTOArwBnG2PqRSRrVO8mVOxJZf7Wk95WyByqbWPJlMEXqx/OtKxE1pbUhLBESqlIFWgguNb++4l+x6/DCgwD9RcsB0qMMaUAIvIo1kijnX3O+ThwvzGmHsAYMz7GRNqBwLTXO3L7Lp+fIw3tvD/ArKMDmZ6VyBNvV9Dc0U1SEM1LSinVK9BRQ0VB3DsfKOuzXw6s6HfOTAARWYu13sE3jTHP9b+RiNwO3A4wefLkIIoyQna+oaiuBnr8BrcrtBlIy+vb8BuCbhqCdxLV7a9uZZEuZq+UGoVARw1FicjdIvK4vd0pIqH4GeoBZgDnYa2J/FsROelbzRjzgDFmqTFmaWZmZghedhh2BtJUmmlqD/3IodHMIeg1rXfkkHYYK6VGKdDO4l8DS4Bf2dsS+9hQKrBWMus1yT7WVzmwyhjTbYw5AOzFCgzhZdcIUqTVkZFDh+usQBDM0NFeU7zxRLlFA4FSatQC7SNYZoxZ2Gf/JRHZOsw1G4AZIlKEFQCuA27od86TWDWBP4hIBlZTUWmAZXKO3UeQRjP1DswlKK9vI8bjInMUs4I9bheF6Qns15FDSqlRCrRG0CMi03p37MlkPUNdYIzxYa1xvAbYBfzdGFMsIveKyFX2aWuAWhHZCbwMfNEY4/wakcOJTsTviiJNnMk3VNHQTn5q3KhXP5uWmahzCZRSoxZojeCLwMsi0vtrvRAYNu2EMWY1sLrfsXv6PDbA5+xt/BDBH5tGaleLIzWCioYO8tPiRn2f6VmJPL+rki6fn2jPiBPJKqUUEHiNYC3wG8AP1NmP33SqUOOBxKXZaxI4UCOobycvJTSBoMdvjqerUEqpYAQaCB4CioBvA7/AmjfwsFOFGg9cCV67aSi0NYKO7h5qWjoHrhG0N0BX4LOZp4+TkUMtnT7+vqGMB17dz95KXSdBqVNNoE1D840xc/vsv2y3609YEp9Ouqsi5KOGjjS0A5Cf2icQdLbAU3fAzifBFQVnfgouuAfcQ388UzOteQjhDAT7Kpu5+ffrOdJoLU/xvX/t5gsXz+KO86eHrUxKqZEJNBC8LSJnGGPWAYjICmCjc8UaB+JSHakRVPQGgt4agTHw+Meg5EU4+9PQUgVr74PuDrj8h0PeKz7aQ35qXNhyDtW0dHLjg29hgMc+eSaF6Ql8+5md/GjNHhJjPNx8VmFYyqWUGplAA8ES4A0ROWzvTwb2iMh2rD7fBY6ULpzivCSbZupbO0N624r6fjWCrY/Avn/DZT+EFZ84/tqsux+K3gVzrhzyftOyEsM2hPSrT2ynob2bp+44mzm51hoJP/3QItq6fHz32V2smOo9vnaCUmr8CrSP4FKsPoKV9lZkH7uSE1cvmzjivUTTTXtbaL9kjzS04xLISYkFXxe89B2YtAyWffydky76FmTNg+e+At3tQ95vWmYC+6ta8ftNSMs5nDf21/DvnZV8+sIZx4MAWGsq/+CaBSTEuLn36Z1YA8OUUuNZQIHAGHNoqM3pQoaFnWbCtIU2A2l5Qzs5ybFEuV2w43FoqoCVXwJXn4/CHQWXfg8aD8PbQ/fJT89KpL27h6NNY7uE9M+e30deSiy3nnNyGqr0xBjuvnAGb+yv5T97q8e0XEqpkdPB54OxZxdLiDOQVtS3k9fbLPT2Q5AxC6a/++QTp66EgjPgjV9Az+D9FL3J58ayw3h7eSPrD9ZxyzlFxEa5BzznxhVTKPDG8dMX9mmtQKlxTgPBYOx8Q3E9jXR0DzmJekQqGtqtjuKGw3D4TVhwLQw2w/icz1q1guInB71fOJLP/WHtARKi3Vy7rGDQc6I9Lj5+7lS2ljXw9mFn0nkrpUJDA8Fg7KahNEI3cqjHbzjW2GF1FG9/3Dp42gcGv2DGxeCdCpv+OOgp6QnRpMZHjVmHcVNHN89sP8r7T5807DKbH1gyiZS4KH73+oExKZtSKjgaCAZjNw2lhnDt4sqmDnx+Y9UIiv9pdRKnFQ5+gcsFiz8Mh16H2v0DniIiTM9MHLMawZodx+jy+Xn/6cMvqhMf7eG6ZQWsKa6kujm0o6+UUqGjgWAw8b1rEoSuRtA7h6AophmObYNZlw1/0cIbQNywefBO47FMPrdq6xEme+MDXgzng0sL6PEbntzcPwO5Umq80EAwGE8Mfk88adIcsnxDvbOKpzWttw4M1EncX3Ku1US05a/gH7ivYnpWIrWtXdS3hj4vUl9VzR2sLanh6kV5AWdOnZ6VyOLJqTy2qUw7jZUapzQQDMHEpZEqrSHLQFpuTyZLP/Y6JGRC9mmBXbjwQ9BSCYfWDvh0b84hp/sJ1uw4ht/AVQvzRnTdB5cUsLeyhW3ljQ6VTCk1GhoIhiDxXlJpDlkfQUVDO944N56Dr8C0C06cOzCUGZdAVALseGLAp6eN0RDSV/fVUOCNY0Z20oiuu3JhLtEeF09tOeJQyZRSo6GBYAiueC9eV+hSUVfUt3NmcjW01ULRysAvjI6HWZfCrlXQ4zvp6fy0OGI8LkdrBL4eP+v213LO9JGvGZ0cG8XKmZms3n50zGdAK6WGp4FgKPFeMkKYeK6ioZ0zo0qsnclnjOziee+zAsiB/5z0lNslTHV45NDW8kaaO32cMz0jqOuvXJDLsaYOnVOg1DikgWAoCZl4aQpJH4Exhor6dk7z77b6B7xTR3aD6RdBdBIUD9Y8lOBoFtLX99UgAmdNSw/q+gvnZBPtcfHMtqMhLplSarQ0EAwlMYtEWmltHf0XbENbN+3dPRS1b7dqAyNdrzgqFmZfDruetpLV9TM9K5Hy+vaQzoLua21JDfPzUkhLiA7q+sQYD+fPspqHerR5SKlxRQPBUBKyrL+to0+cVtHQTib1JLdXWDmEgjHv/dDROGDz0PSsRIyB0urQL1vZ0unj7cP1nDMjuGahXlcsyKOquZONB0ObyE8pNTqOBgIRuVRE9ohIiYh8eYDnPyoi1SKyxd5uc7I8I5ZoBQJPe82ob1Ve385S115rZ6T9A72mnQ8xydas5P5P9Y4ccqB5aP2BWnx+E3T/QK8LZ2cR43Hx7HZtHlJqPHEsEIiIG7gfuAyYC1wvInMHOPVvxphF9vagU+UJil0jiOmsHfVkqIqGdk537cN4YiEnyHV8PDEw+wrY/cxJzUNFGQm4BEdmGL+2r4YYj4slU9JGdZ+EGA/nz8riuR3HdPSQUuOIkzWC5UCJMabUGNMFPApc7eDrhV6iNVTSSwPNnScP2xyJivp2FrgPQvZ88ATXzg5Yo4c6GqH0lRMOx0a5KfDGO1IjWFtSw/Ii76App0fistNyqGruZFMYRw/VtXbxz83l3P9yCX/fWEblGK/loNR4E+hSlcHIB8r67JcDKwY47xoReRewF/isMaas/wkicjtwO8DkyZMdKOog7BpBBo00tHYPm21zKBX1rcyVQ0jOtaMr09TzISbFah6aefEJT013IOdQZVMHeytbuOb0SSG5X+/oodXbj7Ks0BuSewaqy+fnV6+U8OtX9tPp8x8/7nEJN66YzBcumUXSKD5jpU5V4e4sfhootNc8fh7400AnGWMeMMYsNcYszcwc+YSmoEXF4otKJFMaRz272Fd3mCRaIXeUyzt7ou3moWdPah6alpVIaU1rSEflrC2x+kdG21HcKzHGw8qZmfxr+9g2DzV3dHPLHzfwsxf2cdHcbJ656xx23Xspaz7zLj60rICH1x3ifb96g0O1oe9sV2q8czIQVAB9Vy6ZZB87zhhTa4zpzU/8ILDEwfIExReXQUYIAkFq0y7rQc7C0Rdq3vugsxFKXz7h8PTMRLp8fsrr20b/GrbX99WQnhDNnBAuQn/5aTkca+pgc1lDyO45lE5fD7f9aSPrSmv58QcX8ssbTmd+fgpx0W5m5STx3fedxp9vXUFNSyfXPbCOsrrQ/fspdSpwMhBsAGaISJGIRAPXAav6niAiuX12rwJ2OVieoJiELDJoGtXs4rYuH5O7SvDjhuyB+stHaOp5EJty0uihUK9WZozh9ZIazpqegcs1wnkPQ7hwTjbRbhf/GqPRQ1/75w7eOlDH/167kA8sGbiJ66zpGfz1tjNo6+rh5t+vp7E9NLPJlToVOBYIjDE+4E5gDdYX/N+NMcUicq+IXGWfdreIFIvIVuBu4KNOlSdYnqRsMqSRmpbgF1Y50tDOPDlIS1IRRMWFoFDRMPtK2L0afO+Ua2a2FQh2H2se/WsA+6paqGru5Jzpwc0mHkxybBTnzsjgXzuOOZ6a+qktFTy+qZy7L5jO1YuGXkxnbl4yv/3IUg7XtfGZRzfrxDcVMRztIzDGrDbGzDTGTDPGfNc+do8xZpX9+CvGmHnGmIXGmPONMbudLE8wPMm9gSD4pqGKhg7muQ7RlTk/dAXrbR7a/07zUFJsFAXeOHYebQrJS7y+r7d/IPT9MpedlktFQztbHUxNfbSxna//cwdLpqRx94UzArpmeZGXb1w1j5f3VPObVwdeFU6piSbcncXjniRmkSYt1DUH34lYc6yCXKkjKj8E/QO9ilZazUM7T1zYfk5OMrtDFQhKaijKSLDWWA6xi+ZkE+UWR5uHfvTcHjp7/Pz02kV43IH/p/7hFZO5/LQcfvr8XoqP6BoKauLTQDAcey5BV0Nl0LfoObrVutWU00NSJMBuHnqPPXroneahObnJHKhpHXXOoe4eP+tKa0c9m3gwKfFRnD09g2e3H3WkeWhbeQNPbK7gtnOKmJweP6JrRYTvvvc00uKj+ezftjiWv0mp8UIDwXDsuQT+luADQVxtMQDuvFEOHe1v3nuhswlKXjh+aE5uEn4De0bZT7D5cANtXT2c7VAgALh8fi7l9e3sqAhNDaaXMYbvPLOLjMRo/uu8aUHdIy0hmh9+YAF7K1v4yfN7Q1o+pcYbDQTDsfMNudqCTzznbdpNlTsL4kM8gWrqeVag2vzn44fm5FrDPHeNsnno9ZIaXAJnBpl2OhAXz8vG4xJW7wht89Ca4mOsP1jH5y4a3QSx82Zlcf3yAh58rVTXUVATmgaC4diBIKajJugJUJM6SzgWNzOUpbK4o2DRDbB3DTRZX6YFafEkRLtHPXLo9X3VLJiUSkqcczNtU+OjOXNaOqtD2DzU6evhe//azazsJK5dOvrZ0F+9fA45ybF84bGt2kSkJiwNBMNJzAEg09QFNV06tWsAAB5dSURBVLbc195Egf8IzalzQl0yy+kfAdMDW/4CgMslzM5NHtXIoaaObraWN3JuiGYTD+WK03I5VNsWspFOD795iEO1bXz1ijkj6iAeTFJsFN+/ZgGl1a38NAxNRF0+P2+V1vL71w/w0+f38osX9/H01iMcbWwf87KoicvJXEMTQ1QsndFp5PrqqGnpHPHCLPUHtpAphp7s05wpX/o0KDwX3n4IzvkcuFzMzkli1dYjGGOQkS6AA6zbX0uP31j9A9V7YddTULHZWiozKhbSZ0DhOTDzklHPi7h4Xg5ff3IHT26uYF5eyqjuVd/axc9f3MfKmZmsnBm6Ia/vmpnJ9csL+O1rpVwyP4fTJ48uC2sgqpo7+M1/Snni7fJBV8g7fXIqt5xTxGXzc3GHcMKfijwaCALgS8ghu72O6pZOZmQnjeja1kNvkwnETFrkTOEAlnwU/nErlDwPMy9hTm4yf3nrMOX17RR4RzZiBqz+gcVRZSx/7bdwwJ6nkDETErOhswW2/BU2/BZiU2HpLXDOZ6yhrEHwJkRz0dxs/vF2BV+8ZDbRnuB/xd/34j5aOn187YrQ176+evkc/rOnmi8+tpVn7z43JJlYB+Lr8fPAa6X84sUSunv8XDIvh6sW5bF4cioZCTF09fgpqWrhP3ureXxTOXf+dTNzcvfznffOY8mUsU3ipyYObRoKgEnOI0fqg5tUdmwbdSaR9LwRrlE8EnOvhpQCeO0nACyclArA1vIgcvn4/cwovo9/uL+Cq3IbXHgPfH4v3LkBPvoMfPxF+PIh+MgqmLoSXv8J3LfIqpEE2c7/oWUF1LV28cKu4Edm7a9u4c/rDnH98snMHGGwDkRvE9H+6lZ++oIzTURldW188Ddv8sPn9nDujAxe+NxK7r/xdC6Zl0NWUiwulxAb5WZ+fgp3nD+dFz63kl9cv5jGti6u+fWbfOOpHdqPoYKigSAAnpR8cqSOmuaRp5mIr91Jsb+Q/LSR/zIPmDsKzroLytbBoTeYnZtEjMfFlsMjDASdLbT/5UZu6vo7JblXwl2b4NzPQ1L2ya83dSVc+xB84lXImgOr7oK/fPB4p/VInDsjk7yUWB5Zf3jE1/b63urdxEa5+exFDnTK2941M5PrlhXw21dDP4rordJarvrl65RUtXDfdYv4zU1LKMxIGPIat0t4z8I8Xvj8Sj52diF/evMQ771/LfsqQ5NiREUODQQBiE7LJ0OaqG8a4f9gPd14W0s44JlKXLQzTQnHLb4J4jPgle8R5RJOy08ZWXbPhjL4/aXE7H+Oe7tvQq6+H+ICaAvPXQg3PwOX/QgOvg6/OgOKnxz+uj7cLuGDSwt4vaQmqMyfb5TU8MKuSj51/jQyEmNGfP1IfO0KaxTRF0M4iujR9Ye58cG3SEuI5qk7zubqRfkj6tuJj/bwjffM4w8fW0Z1cyfv+eXrPL6pPCRlU5FBA0EAXClWsrKuhiMju7B6Dx7TTVXCLAdK1U90PKz8bzjwKux+lkUFqWyvaKSrzwIsgypbD7+9ABoOcX/Od3ku8X1MH0nzissFK26H/1oL3qnw2M3w1B1Wf0KArltegMclPPhaaeCvizUD+ptPFzMpLY5bzi4a0bXBSIqN4nt2E9F3nx1dslxfj59vPV3Ml5/YzlnTM/jnp85mqr32dDDOn5XFvz59LosKUvnCY1v578e30t6lTUVqeBoIApFsZ8tuGmEgOLYNgJa0EKSeDsTSWyBzNqz5CstyPXT5/Ow+NsywzK2Pwh+vgOgEuj/2bx44Oo2VszKDGm1E+jS49d9w7hdg81/gN+dCxaaALs1NieN9i/N5dEMZ1SNognv4zUPsrWzhf66c61gHbn8rZ2Zy+7um8vC6Q/x53aGg7tHU0c1tD23kD2sP8rGzC/n9zUtDMmcjKzmWv9x2BnddMJ3HNpXzvl+tZb8Dy5cOprmjm11Hm3hlTxXP7TjK8zsrWVday5GGdl2nehzTUUOBSMoDwN16bESXmaNbaTcxeLICy3w5au4oeM/P4Q+Xce6e7wDXsvlwAwvszuMT9PjgxW/BGz+3hp9e+xBvHzM0d5aObuilOwou/B+YdgE8cTv87mI4/6tw9mfANfQX9SdXTuOxTeX8fu0BvnTp7GFfqqq5g5++sJdzZ2Rw8dzsYc8PpS9dOpt9lc18c1Ux+alxnD87K+Bry+rauPVPGyitbuX/ve80blgR2uVX3S7h8xfPYmmhl8/+bQtX/eJ1vnfNAq5amBfS1wHo8RvWldbyzLYjbDxYT0l1y6BjBhKi3Zw+JY0zpqZzybxspmeFvlNfBUcDQSCSrf+BYtpGNqrFd2Qbu00BuWnBV/dHbPIKuOBrxL94L9+Ohw0H87j5rMITz2ksh398HA6/AUtvhct+AO4o/rN3N26XcFYo8gsVng3/9To881l48V4oeRHe93+QOviX3tTMRK5ckMcf1h7gxhWTmTREB7sxhi89vo0un59vXjUvuBrMKLhdwn3XL+b6B9bxiYc38csbFnPxvJxhr/vX9qN86R9WTfFPtyx3NJfTypmZPHv3Odz1183c/chm1h+o5WuXzx11f5Uxhp1Hm3hqyxGe2lJBZVMniTEelhd5ec/CPKZlJpKTEkNclAe/MdS3dXG4ro3dR5vZcLCOH63Zw4/W7GFGViJXLczj2mUFZCfHhuhdq2BoIAhEbApdrliSuqro8ZvAJu8Yg6tyB8X+5Y6kcR7SOZ+Dxgpu2vg7pu07jDn8AyR7rhUAtj4K6x+wznv/b2HBtccv+8/eapZMTiM5VAu4x6XBB/4AMy6G1V+EXy6DM++05h3EDPxr8MuXzeaFnZXc+/ROfnPTkkG/4P+6/jAv76nmG++Zy7RRtKuPRnJsFH+97Qw+8of1fOLPm7jrghnccf40Yjwnf9Eea+zgB8/t5p+bK1gwKYWfX7d42FFBoZCbEscjt5/Bj9fs4TevlvLy7mq+dsUcLpufM+LgWdHQzlNbKnhycwV7K1vwuITzZmVxz5X5XDgnK+CmucqmDp7bcYxntx3lf5/fy89e3McFs7O4Yflk3jUzUyfHhYE4vUJUqC1dutRs3LhxzF+34UeLWdeUzulffIasQH691B2Any/iK923ctMd32RuXujW/A2IMbz92PeZXnwfydInHYG4YM5VcPG3T/h1Xl7fxjk/eJn/vnQWnzpveujL03AYXvgW7Hjcmoi25GarNpI25aRTf/3Kfn7w3G6+8975fPiMk59fV1rLTb97izOmpvOnjy0P6TKawWjr8vH1J3fwxNsVFHjjuH75ZJYVeomPdnO4to0XdlXxzLYjGOAT75rKXRfMGNXEuWCtP1DHPU/tYPexZublJXPbuUVcPDeHhJjBfw8eaWhnTfExVm8/yoaD1pDZpVPSeO/ifK44LXfEM+37O1TbyqMbynhsYxk1LV3kp8ZxzZJJfOD0SSNOH66GJiKbjDFLB3xOA0Fgqn9zNdUVB/Dd/urAbe79FT8Jj93MlZ3f4ZFvfHJUWTCDVdHQzmXff5r7ltZxfk4HJGRaGUtTTl6y8cHXSvnOs7t4+QvnUeTkL9WKTbD2Ptj1NBi/Nfx02gXW38w5kJBJT0wyH39oI+v2V/OLD87hwilR0N4A7fXsPnCYR14rJiu2h1tXZBNrOqz7umPAEwNxqVZ+qKQcK8gEMgQ2RF7bV819L+xj46ET5xgkRLu5enE+/7VyWlAzvUPJ1+PnH2+X88CrpeyvbiXa42JFkZc5ucmkJ0Tjcbuob+2irL6Ntw/XU1Zn/YiYnZPEFaflcvWifEe+oLt8fl7YVckj6w/zekkNxsCKIi/XnD6JC+ZkhWRYcFuXjwM1rRyoaaW0upUjDe3Ut3VR39ZNW5fv+HnxUR7SEqLwJsSQmxLL1MwEpmYkMjUzYcwGJDhBA0EI1Pz900QX/403P7iZS+bnDn/BC9/Et/YXnC0P89Y9lztfwEGc/+NXmJqRwO8+umzI86759Ru0dfXwr0+fOzYFayiD7Y/BntVQ8baVOC8Y4gYR8PsGfj4xB7JmW0EmbzFMWmoNcXWwT+FoYzt7jjXT0e0nOzmGeXkpYakBDMXvN7x1oI4XdlXyxv5a9le3HB9q7HYJ2UkxLJiUypIpaVwwJ2tMm9+ONLTzz83WWtMHaloRgQWTUjl7Wjqn5acwNy+ZnJTYAZvgOrp7ONrYwYGaFg7UtHGwppXSmhZKq1s52thxwrmZSTGkxUeRGh9NYowHAQzQ2umjvq2LutauE7IJuARmZCWxYFIKCwpSWTgphdk5yePusx3MUIHA0T4CEbkUuA9wAw8aY74/yHnXAI8Dy4wxY/8tH4CYrKkk7WynobYSCCAQHN1KmaeQ3NTRJVIbrXNnZPDYxnLau3oG7SQ82tjOpkP1fM7BWbknSS2Acz9nbd0dUL0LavdDaw10NIC46PbDaweaeemwj8quWHwxqZw1fxo3vus04hOSIDoB3NF2IOixVmprr4fmY9B8BOpKoWqXtW36I7z1a+u149IgfwnkL7UCQ/6SkK4VkZsSR27KGPcLjZDLJZw5Lf34ehM9fkNHdw/dPX6SYqPC2k6flxrHHedP51PnTaP4SBMv767ipT1VPPBqKb4+Q1DT4qOON2v5/Yb6tm7a+03yS4r1MDUzkTOnplOUkcDUzESKMhIoykgIqNO8txZRWt3KvspmtlU08uLuKh6zJ+xFu13Myklifn4y8/JSmJ+fwuycpFOu5uBYIBARN3A/cBFQDmwQkVXGmJ39zksCPg285VRZQiEh22o376wuBYZJIGcMHN1KsX8xk8PcFHDpvBweevMQr+yp4rLTBg5gj2+0/qN2YnhhQKJirV/reYtPPAxccD6s9Bsa27tJiRviC8rltibVRcfbTV9LTny+xwfVu6FiI5RvtJqoSn6A9RsQK1dTzgLIOQ1yF0D2PEieBO7IGE/hdsmQfQXhICLMz7e+XO+6cAYd3T3sOdbM7mNNHGvspKq5w/riN1ZgS4uPIi0hmqykWIoy4inKSCQtPmpUI8rioz3My0s5ITOuMYby+na2ljewvbyRHUcaWb39GI+sLwOsf8sp6fEUpMVT4I2jIC0eb0I0KXFRJMdFEdOnBmGA9q4eWjp9tNpbc6ePlg4fzR0+Wjp9NHd009RhHbvzgulcPsj/x6Ph5Ce/HCgxxpQCiMijwNXAzn7nfRv4AfBFB8syai5vofWg/uDwJzeWQ1st630FTAlzh9fyIi/pCdE8s/3ogIHA7zf8bWMZZ01LH5NRLMFwuwTvKDslcXsgZ761LfmodayzGY5stpqmjm2Do9uspqre4ODyWB3qaYXWlpAFCRlW7SE+w+qPiEqwgk9UvF1DGfu+oEgRG+VmYUEqCwsC6KNzkIhQ4I2nwBvPlQusH0+9waH4SCPbKxrZX9VKWX0bmw/X09QxSLPlENwuISnWY20xUSTGeshLjSXeoVQ1TgaCfKCsz345sKLvCSJyOlBgjHlWRAYNBCJyO3A7wOTJoZ18E7BUa/RKdHMAidHsxeq39RRxQ5hrBB63iysW5PLohjJqWzpJ79fp9uq+asrr2wOawDXhxCRB0busrVdnC1QWW01V9Qetre4AHNkC7XXD39MVZddMEq0tJtEKENFJfR7bz8Wl2UHFC/Hp72yjXONBjb2+weHSfn2Ije3dNLZ109RhbZ3dfuhTSYmLcpMY4yExxkOC/Tc2yjWmc2PCVhcUERfwE+Cjw51rjHkAeACszmJnSzaImESa3GkktQWQzOvoVoy42WUmh71pCOCmM6bw0JuHeHRDGXec/87QUGMMv3yphJzkWC6eN7Yzc8etmERrUt7kFSc/1+OzgkFbrd2X0QjdbdDVav9tg+5W629XK3Q1W387W6DtEHS1WI+7WsDXcfL9e0XF20HBDhBxXito9N/i+xyPTY2YZqxTTUpclKNLvoaCk//lVAAFffYn2cd6JQHzgVfsyJcDrBKRq8Zrh3FTbD7pLUeHX/nr6BYaEqbS2R4d9qYhgBnZSZw7I4MHXyvlwyumkBJv/Uf5/M5KNh6q59tXzxtwBIbqx+2x1rBODDydxKB6fFYgaauxAssJW92J+/UHrU7w9gaON1sNxBNnN1Ml2DWP3iarRLuWkmDte2Kscz0xVu0joP3YEzfXqTFSRgXGyUCwAZghIkVYAeA64IbeJ40xjcDx+fUi8grwhfEaBADak6YwpeVNmjp8g0d4Y+DIFspjlxLtcZGdND6mzn/lsjlc+YvX+NYzxfzvBxdyrKmDr/5zB7NzkvjQsjA1t0UytwcS0q0tUH4/dDbaQaH+neDQVmc97mrpUztpsWsobdBU/k4tpbvdqo30jHxtjRPLH90nWPQLEsf3RxBweq9zR1vNay6P9W/kirL6XVwe+2+f/b7HNDCNimOBwBjjE5E7gTVYw0d/b4wpFpF7gY3GmFVOvbZTejJmkXvsWXZXVpJSOGngkxoOQ2sVO+JnUJAWF/ZZr73m5iVz1wUzuO/FfeyvaqG8vp1On5+fXbfolBkHHfFcrneagkbL77eCQXe7NezWZ//tv+/rsIb3+vpsg+73ua6jafD7OkFcAweNIQOKp885UdbIM3H3++vqs+8Z4Nhg5wZxD3FZn7H03dwn7qdNCU2NtB9HGxWNMauB1f2O3TPIuec5WZZQiMubBzug4dA2GCwQlFmjYF/tmEZh+vgahfOZd8/AmxDNY5vKWFiQyhcvmcXsnDFOfaHGB5cLXHFj3zFtDPR0DR5QerrA323NC+npth73dFsTBo/v++xzfIM/17t/wn18J97P1wX+Vvt6n/XX9FjXmB4rWJ6w7xvgWA9DNteF2hU/gWW3hvy22rs0AmlFCwHoPLoTGGS2cNlbmOhEXq7P4OY54UmGNhgR4eazCk/ORqrUWBGxm4JiIDa8ky1DxpgTA8Pxv/4Bjg8STEyPdR/jf2frvUffLdOZ0X0aCEYgOXsa7UTjqR1i8fLDb9GRvZiOfYQtK6ZSagyJ2CO2Tt2vU20cHgmXi3LPZFKbBgkEnc1QVczRZKvmMC1rfDUNKaXUQDQQjNDRhLkUdu622wb7OfQmGD+7ouYBWiNQSp0aNBCMUHPGIhJop6dqz8lP7n8JPLGs7ZpORmI0qfGjTIuglFJjQAPBCJl8K4tr4743Tn5y/0sw5Wz21PqYqrUBpdQpQgPBCHkL5tBgEvAd6BcI6g9CzR7MtPMpqWrRZiGl1ClDA8EIFWYm8Zr/NJLKXz6xn2DHEwBU5V9MY3s3c3IHXpNXKaXGGw0EI5SbEsur7hXEddVB+QbroDGw4x8waTnbW60UufPGeo1ipZQKkgaCERIRKjPPxYcHtj9uHTz8JlTugIUfovhIEyLojF2l1ClDA0EQJuXl8AznYjb/Gar3wr//x1qoZOEN7DzaSFF6wrhb7UkppQajgSAIs7KT+HHnezHuGLh/mbX84eU/hOh4dlQ0MVebhZRSpxD92RqEObnJlJtM1p/3MGc0PAtFK2H25VQ0tFPR0M5t5xaFu4hKKRUwDQRBOC0/hSi38EpDFmdc9oPjxzccsJYyXFboDVfRlFJqxLRpKAhx0W7m56ew8eCJa9iuP1hHUoyHObnaNKSUOnVoIAjSskIv28ob6ei25hIYY1hbUsPSwjTc42QxGqWUCoQGgiCdMdVLV4+fdaW1AOytbOFQbRvvnquLwCulTi0aCIJ09vQMkmI8PLPtKABPbC7HJXDRHA0ESqlTiwaCIMV43Fy5MI9VW4+wrbyBR9eXccm8HLKSx8di9UopFSgNBKPwqfOm4XEJV/1yLe3dPXz2opnhLpJSSo2Yo4FARC4VkT0iUiIiXx7g+U+KyHYR2SIir4vIXCfLE2oF3nj+ctsKPrBkEr+7eSkzszXRnFLq1OPYPAIRcQP3AxcB5cAGEVlljNnZ57S/GmP+zz7/KuAnwKVOlckJiyensXhyWriLoZRSQXOyRrAcKDHGlBpjuoBHgav7nmCMaeqzmwAYB8ujlFJqAE7OLM4HyvrslwMr+p8kIncAnwOigQsGupGI3A7cDjB58uSQF1QppSJZ2DuLjTH3G2OmAV8Cvj7IOQ8YY5YaY5ZmZmaObQGVUmqCczIQVAAFffYn2ccG8yjwXgfLo5RSagBOBoINwAwRKRKRaOA6YFXfE0RkRp/dK4B9DpZHKaXUABzrIzDG+ETkTmAN4AZ+b4wpFpF7gY3GmFXAnSLybqAbqAdudqo8SimlBuZoGmpjzGpgdb9j9/R5/GknX18ppdTwwt5ZrJRSKrzEmFNr6L6IVAOHgrw8A6gJYXFOBfqeI4O+58gwmvc8xRgz4LDLUy4QjIaIbDTGLA13OcaSvufIoO85Mjj1nrVpSCmlIpwGAqWUinCRFggeCHcBwkDfc2TQ9xwZHHnPEdVHoJRS6mSRViNQSinVjwYCpZSKcBETCIZbLe1UJSIFIvKyiOwUkWIR+bR93Csiz4vIPvtvmn1cROTn9r/DNhE5PbzvIDgi4haRzSLyjL1fJCJv2e/rb3Z+K0Qkxt4vsZ8vDGe5gyUiqSLyuIjsFpFdInJmBHzGn7X/m94hIo+ISOxE/JxF5PciUiUiO/ocG/FnKyI32+fvE5ERpeuJiEDQZ7W0y4C5wPWn2rKYQ/ABnzfGzAXOAO6w39uXgReNMTOAF+19sP4NZtjb7cCvx77IIfFpYFef/R8APzXGTMfKW3WrffxWoN4+/lP7vFPRfcBzxpjZwEKs9z5hP2MRyQfuBpYaY+Zj5Su7jon5Of+Rk1dmHNFnKyJe4BtYa74sB77RGzwCYoyZ8BtwJrCmz/5XgK+Eu1wOvdensJYH3QPk2sdygT32498A1/c5//h5p8qGldL8RayFjJ4BBGu2paf/542V9PBM+7HHPk/C/R5G+H5TgAP9yz3BP+Peha289uf2DHDJRP2cgUJgR7CfLXA98Js+x084b7gtImoEDLxaWn6YyuIYuzq8GHgLyDbGHLWfOgZk248nwr/Fz4D/Bvz2fjrQYIzx2ft939Px92s/32iffyopAqqBP9jNYQ+KSAIT+DM2xlQAPwYOA0exPrdNTOzPua+Rfraj+swjJRBMeCKSCPwD+Iw5cS1ojPUTYUKMExaRK4EqY8ymcJdlDHmA04FfG2MWA62801QATKzPGMBu1rgaKwjmYa1p3r/5JCKMxWcbKYFgpKulnVJEJAorCPzFGPOEfbhSRHLt53OBKvv4qf5vcTZwlYgcxFrV7gKs9vNUEelNq973PR1/v/bzKUDtWBY4BMqBcmPMW/b+41iBYaJ+xgDvBg4YY6qNMd3AE1if/UT+nPsa6Wc7qs88UgLBsKulnapERIDfAbuMMT/p89Qq3lno52asvoPe4x+xRx+cATT2qYKOe8aYrxhjJhljCrE+x5eMMTcCLwMfsE/r/357/x0+YJ9/Sv1yNsYcA8pEZJZ96EJgJxP0M7YdBs4QkXj7v/He9zxhP+d+RvrZrgEuFpE0uzZ1sX0sMOHuJBnDzpjLgb3AfuBr4S5PCN/XOVjVxm3AFnu7HKt99EWs5T9fALz2+YI1gmo/sB1rVEbY30eQ7/084Bn78VRgPVACPAbE2Mdj7f0S+/mp4S53kO91EbDR/pyfBNIm+mcMfAvYDewAHgZiJuLnDDyC1Q/SjVX7uzWYzxa4xX7/JcDHRlIGTTGhlFIRLlKahpRSSg1CA4FSSkU4DQRKKRXhNBAopVSE00CglFIRTgOBilh2Rs9P9dnPE5HHHXqtKBF5e4DjH7Szib7sxOsqFQgNBCqSpQLHA4Ex5ogx5gNDnD8a5wBrBzh+K/BxY8z5Dr2uUsPSQKAi2feBaSKyRUR+JCKFvTnhReSjIvKknQv+oIjcKSKfs5O+rbPT/iIi00TkORHZJCKvicjsQV7rUuBffQ+IyD1YAeJ39uvPE5H1dnm2icgMB9+7UsdpIFCR7MvAfmPMImPMFwd4fj7wfmAZ8F2gzVhJ394EPmKf8wBwlzFmCfAF4FeDvNb5wCt9Dxhj7sWaLXyj/fqfBO4zxiwClmLNMlXKcZ7hT1EqYr1sjGkGmkWkEXjaPr4dWGBnfD0LeMxKhwNYaRBOYC+yUmeMaRvm9d4EviYik4AnjDH7QvEmlBqO1giUGlxnn8f+Pvt+rB9RLqz8+Iv6bHMGuM+lBJAAzBjzV+AqoB1YLSIXjKr0SgVIA4GKZM1AUrAXG2vdhwMi8kE4vp7swgFOPal/YCAiMhUoNcb8HCvb5IJgy6bUSGggUBHLGFMLrBVrcfQfBXmbG4FbRWQrUIy1mMpx9nrZ040xuwO417XADhHZgtU/8VCQZVJqRDT7qFIOEpFzgA8bYz4Z7rIoNRgNBEopFeG0aUgppSKcBgKllIpwGgiUUirCaSBQSqkIp4FAKaUinAYCpZSKcP8fN8uAhGxYW1cAAAAASUVORK5CYII=\n"
1090 | },
1091 | "metadata": {
1092 | "needs_background": "light"
1093 | }
1094 | }
1095 | ]
1096 | }
1097 | ]
1098 | }
--------------------------------------------------------------------------------