Example 15: Real space parallelization

Warning

Real space parallelization is numerically unstable for some systems. We recommend to use serial simulation. The result is not always reproducible because we are under the construction of the code.

  • This tutorial is based on the same system as the example 12.

  • Since real space parallelization requires MPI, this tutorial is based on the script singlet_fission_nprocs.py rather than Jupyter notebook.

  • For example, one can run the script with export OMP_NUM_THREADS=2 && mpirun -np 4 uv run singlet_fission_nprocs.py

  • JAX backend cannot be used for MPI.

0. Environment

$ uv sync --extra mpi  # or simply install mpi4py manually
$ export OMP_NUM_THREADS=2
$ mpirun -np 4 uv run singlet_fission_nprocs.py

1. Import modules

from mpi4py import MPI
rank = MPI.COMM_WORLD.Get_rank()
size = MPI.COMM_WORLD.Get_size()


import platform
import sys

import pytdscf


print(sys.version)
print(f"pytdscf version = {pytdscf.__version__}")
print(platform.platform())

import math
from itertools import chain

import numpy as np
import sympy
from pympo.utils import import_npz

from pytdscf import (
    BasInfo,
    Boson,
    Exciton,
    Model,
    Simulator,
    TensorHamiltonian,
    TensorOperator,
    units,
)

2. Define MPO and Basis

Note: MPO is defined in example 12.

n_order = 61 # number of sites for each side of the exciton
basis = [Boson(8)] * n_order + [Exciton(nstate=3, names=["S1", "TT", "CS"])] + [Boson(8)] * (2 * n_order)
basinfo = BasInfo([basis])
ndim = len(basis)
print(ndim)
sys_site = n_order
backend = "numpy" # "jax" is not supported for MPI.


if rank == 0:
    # Hamiltonian prepared in example 12.
    pot_mpo = import_npz("singlet_fission_mpo.npz")
    potential = [
        [{tuple((k, k) for k in range(ndim)): TensorOperator(mpo=pot_mpo)}]
    ]  # key is ((0,0), 1, 2, ..., ndim-1)
else:
    potential = None

H = TensorHamiltonian(
    ndof=len(basis), potential=potential, kinetic=None, backend=backend
)

if rank == 0:
    core = np.zeros((1, 3, 1))
    core[0, 0, 0] = 1.0
    potential = [[{(sys_site,): TensorOperator(mpo=[core], legs=(sys_site,))}]]
else:
    potential = None
s1 = TensorHamiltonian(
    ndof=len(basis),
    potential=potential,
    kinetic=None,
    backend=backend,
)
operators = {"hamiltonian": H, "S1": s1}

sites = chain(range(sys_site-1, -1, -4), range(sys_site+1, 3 * n_order + 1, 8), range(sys_site+2, 3 * n_order + 1, 8))
for isite in sites:
    if rank == 0:
        core = np.zeros((1, basis[isite].nprim, 1))
        core[0, :, 0] = np.arange(basis[isite].nprim)
        potential=[[{(isite,): TensorOperator(mpo=[core], legs=(isite,))}]]
    else:
        potential = None
    n = TensorHamiltonian(
        ndof=len(basis),
        potential=potential,
        kinetic=None,
        backend=backend,
    )
    operators[f"N{isite}"] = n

3. Define Model and initial state

model = Model(basinfo=basinfo, operators=operators)
model.m_aux_max = 1 # Initial bond dimension
# Starts from S1 state
init_boson = [[1.0] + [0.0] * (basis[1].nprim - 1)]
model.init_HartreeProduct = [init_boson * n_order + [[1.0, 0.0, 0.0]] + init_boson * (2*n_order)]

4. Run simulation

nproc = size
D=30
proj=7
svd=6

jobname = f"singlet_fission_{D}D-0{proj}proj-0{svd}svd-{nproc}cores"
simulator = Simulator(jobname=jobname, model=model, backend=backend, verbose=2)
simulator.propagate(
    maxstep=2000,
    stepsize=0.2, # This value affects the accuracy of the simulation.
    reduced_density=(
        [(sys_site, sys_site)],
        10,
    ),  # we want to know diagonal_element of (|S1><S1| |CT><CT| |TT><TT| |S1><CT| |S1><TT| |CS><TT|)
    energy=False,
    autocorr=False,
    observables=True,
    observables_per_step=10,
    parallel_split_indices=[(184*k//nproc, 184*(k+1)//nproc-1) for k in range(nproc)],
    adaptive=True,
    adaptive_Dmax=D,
    adaptive_dD=D,
    adaptive_p_proj=pow(10, -proj),
    adaptive_p_svd=pow(10, -svd),
)

After the simulation

Note: This is not MPI parallelization.

  • compare_elapsed_time.py is a script to visualize the elapsed time of parallelization.

  • visualize.ipynb is a Jupyter notebook to visualize the result.