示例#1
0
def test_drude():
    from minitn.heom.noise import Drude
    from minitn.lib.units import Quantity

    # System
    e = Quantity(100, 'cm-1').value_in_au
    v = Quantity(50, 'cm-1').value_in_au

    # Bath
    corr2 = Correlation(k_max=1)
    corr2.symm_coeff  = [0.0]  # [4.66691921e+01 * 9.24899189e+01]
    corr2.asymm_coeff = [0.0]  # [4.66691921e+01 * -2.35486582e+01]
    corr2.exp_coeff = [1.0]
    corr2.delta_coeff = 0.0 # delta_coeff()
    corr2.print()

    h = np.array([[e, v],
                  [v, 0]])

    op = np.array([[1, 0],
                   [0, -1]])


    # Superparameters
    max_tier  = 5 # (number of possble values for each n_k in the extended rho)

    heom = Hierachy([max_tier], h, op, corr2)

    phi = np.array([1, 0]) 
    rho_0 = np.tensordot(phi, phi, axes=0)
    init_rho = heom.gen_extended_rho(rho_0)

    solver = MultiLayer(init_rho, heom.diff())

    # Define the obersevable of interest
    dat = []
    for n, (time, r) in enumerate(solver.propagator(
        steps=20000,
        ode_inter=0.1,
    )):
        if n % 100 == 0:
            rho = np.reshape(r, (-1, 4))
            for n, _ in enumerate(rho):
                if n == 0:
                    flat_data = [time] + list(rho[0])
                    dat.append(flat_data)
                    print('Time: {}; rho: {}  {}  {}  {}'.format(*flat_data))
    np.savetxt('test.dat', np.array(dat, dtype=np.complex128))
    return np.array(dat)
示例#2
0
def test_heom(fname=None):
    ph_dims = list(np.repeat(model.ph_dims, 2))
    n_dims = ph_dims if model.bath_dims is None else ph_dims + model.bath_dims
    print(n_dims)

    root = tensor_tree_template(rho_0, n_dims, rank=rank_heom)
    leaves = root.leaves()
    h_list = model.heom_h_list(leaves[0], leaves[1], leaves[2:], beta=beta)

    solver = MultiLayer(root, h_list)
    solver.ode_method = 'RK45'
    solver.cmf_steps = solver.max_ode_steps  # use constant mean-field
    solver.ps_method = 'split'
    #solver.svd_err = 1.0e-10

    # Define the obersevable of interest
    logger = Logger(filename=prefix + fname, level='info').logger
    logger2 = Logger(filename=prefix + "en_" + fname, level='info').logger
    for n, (time, r) in enumerate(
            solver.propagator(
                steps=count,
                ode_inter=dt_unit,
                split=True,
            )):
        # renormalized by the trace of rho
        norm = np.trace(np.reshape(np.reshape(r.array, (4, -1))[:, 0], (2, 2)))
        r.set_array(r.array / norm)
        if n % callback_interval == 0:
            t = Quantity(time).convert_to(unit='fs').value
            rho = np.reshape(r.array, (4, -1))[:, 0]
            logger.info("{}    {} {} {} {}".format(t, rho[0], rho[1], rho[2],
                                                   rho[3]))
            en = np.trace(np.reshape(rho, (2, 2)) @ model.h)
            logger2.info('{}    {}'.format(t, en))
    return
示例#3
0
def test_uni():
    from scipy.integrate import solve_ivp
    # System
    e = Quantity(100, 'cm-1').value_in_au
    v = Quantity(50, 'cm-1').value_in_au

    h = np.array([[e, v],
                  [v, 0]], dtype='double')

    phi = np.array([1, 0], dtype='complex') 
    ode_int = 0.1
    diff = lambda t, y: (h @ y) * (-1.0j)
    
    for n in range(20000):
        time  =  ode_int * n
        solver = solve_ivp(
            diff, (time, time + ode_int), phi, method='RK23', max_step=ode_int,
            rtol=1e-8, atol=1e-8
        )
        phi = solver.y[:, -1]
        if n % 100 == 0:
            print('Time: {}; rho: {}    {}'.format(time, np.abs(phi[0])**2, np.abs(phi[1])**2))
示例#4
0
def test_uni_int():
    from scipy.integrate import solve_ivp
    from scipy.linalg import eigh
    # System
    e = Quantity(100, 'cm-1').value_in_au
    v = Quantity(50, 'cm-1').value_in_au

    h = np.array([[e, v],
                  [v, 0]], dtype='double')

    l, u = eigh(h)
    uc = np.conj(np.transpose(u))

    phi = np.array([1, 0], dtype='complex') 
    ode_int = 0.1
    
    data = []
    for n in range(20000):
        time  =  ode_int * n
        vec =  np.dot(uc, (np.exp(-1.0j * time * l) * np.dot(u, phi)))
        if n % 100 == 0:
            data.append([time] + list(np.abs(vec)**2))
    np.savetxt('reference.dat', np.array(data, dtype=np.complex128))
示例#5
0
def test_mctdh(fname=None):
    sys_leaf = Leaf(name='sys0')

    ph_leaves = []
    for n, (omega, g) in enumerate(ph_parameters, 1):
        ph_leaf = Leaf(name='ph{}'.format(n))
        ph_leaves.append(ph_leaf)

    def ph_spf():
        t = Tensor(axis=0)
        t.name = 'spf' + str(hex(id(t)))[-4:]
        return t

    graph, root = huffman_tree(ph_leaves, obj_new=ph_spf, n_branch=2)
    try:
        graph[root].insert(0, sys_leaf)
    except KeyError:
        ph_leaf = root
        root = Tensor()
        graph[root] = [sys_leaf, ph_leaf]
    finally:
        root.name = 'wfn'
        root.axis = None

    stack = [root]
    while stack:
        parent = stack.pop()
        for child in graph[parent]:
            parent.link_to(parent.order, child, 0)
            if child in graph:
                stack.append(child)

    # Define the detailed parameters for the ML-MCTDH tree
    h_list = model.wfn_h_list(sys_leaf, ph_leaves)
    solver = MultiLayer(root, h_list)
    bond_dict = {}
    # Leaves
    for s, i, t, j in root.linkage_visitor():
        if t.name.startswith('sys'):
            bond_dict[(s, i, t, j)] = 2
        else:
            if isinstance(t, Leaf):
                bond_dict[(s, i, t, j)] = max_tier
            else:
                bond_dict[(s, i, t, j)] = rank_wfn
    solver.autocomplete(bond_dict)
    # set initial root array
    init_proj = np.array([[A, 0.0], [B, 0.0]]) / np.sqrt(A**2 + B**2)
    root_array = Tensor.partial_product(root.array, 0, init_proj, 1)
    root.set_array(root_array)

    solver = MultiLayer(root, h_list)
    solver.ode_method = 'RK45'
    solver.cmf_steps = solver.max_ode_steps  # constant mean-field
    solver.ps_method = 'split'
    solver.svd_err = 1.0e-14

    # Define the obersevable of interest
    logger = Logger(filename=prefix + fname, level='info').logger
    logger2 = Logger(filename=prefix + 'en_' + fname, level='info').logger
    for n, (time, r) in enumerate(
            solver.propagator(
                steps=count,
                ode_inter=dt_unit,
                split=True,
            )):
        if n % callback_interval == 0:
            t = Quantity(time).convert_to(unit='fs').value
            rho = r.partial_env(0, proper=False)
            logger.info("{}    {} {} {} {}".format(t, rho[0, 0], rho[0, 1],
                                                   rho[1, 0], rho[1, 1]))
            en = np.trace(rho @ model.h)
            logger2.info('{}    {}'.format(t, en))
示例#6
0
from __future__ import absolute_import, division, print_function

from builtins import filter, map, range, zip
from minitn.heom.corr import Drude

from minitn.heom.network import tensor_tree_template
from minitn.heom.propagate import MultiLayer
from minitn.lib.backend import DTYPE, np
from minitn.lib.logging import Logger
from minitn.lib.tools import huffman_tree
from minitn.lib.units import Quantity
from minitn.models.sbm import SBM
from minitn.tensor import Leaf, Tensor

# System: pure dephasing
e = Quantity(5000, 'cm-1').value_in_au
v = Quantity(500, 'cm-1').value_in_au
max_tier = 20
rank_heom = 10
rank_wfn = 8
beta = None
prefix = 'drude_boson_tree_zt_t{}_'.format(max_tier)

ph_parameters = [
    #(Quantity(400, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    #(Quantity(800, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    #(Quantity(1200, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    (Quantity(1600, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
]
dof = len(ph_parameters)
示例#7
0
# coding: utf-8
from __future__ import absolute_import, division, print_function

from builtins import filter, map, range, zip

from minitn.heom.network import simple_heom, tensor_train_template
from minitn.heom.propagate import MultiLayer
from minitn.lib.backend import DTYPE, np
from minitn.lib.logging import Logger
from minitn.lib.tools import huffman_tree
from minitn.lib.units import Quantity
from minitn.models.sbm import SBM
from minitn.tensor import Leaf, Tensor

# System: pure dephasing
e = Quantity(5000, 'cm-1').value_in_au
v = Quantity(500, 'cm-1').value_in_au
dof = 1
max_tier = 12
rank_heom = 1
rank_wfn = 8
prefix = '{}-DOF_2site_t{}_'.format(dof, max_tier)

ph_parameters = [
    #(Quantity(250, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    (Quantity(750, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
]

model = SBM(
    sys_ham=np.array([[-0.5 * e, v], [v, 0.5 * e]], dtype=DTYPE),
    sys_op=np.array([[-0.5, 0.0], [0.0, 0.5]], dtype=DTYPE),
示例#8
0
def sbm_ft(including_bath=False, snd=False):
    # Define parameters of the model.
    sbm = SpinBosonModel(
        including_bath=including_bath,
        e1=0.,
        e2=Quantity(6500, 'cm-1').value_in_au,
        v=Quantity(500, 'cm-1').value_in_au,
        omega_list=[Quantity(2100, 'cm-1').value_in_au],
        lambda_list=([Quantity(750, 'cm-1').value_in_au]),
        dim_list=[10],
        stop=Quantity(10000, 'cm-1').value_in_au,
        n=32,
        dim=30,
        lambda_g=Quantity(2250, 'cm-1').value_in_au,
        omega_g=Quantity(500, 'cm-1').value_in_au,
        lambda_d=Quantity(1250, 'cm-1').value_in_au,
        omega_d=Quantity(50, 'cm-1').value_in_au,
        mu=Quantity(250, 'cm-1').value_in_au,
        tau=Quantity(3, 'fs').value_in_au,
        t_d=Quantity(6, 'fs').value_in_au,
        omega=Quantity(13000, 'cm-1').value_in_au,
    )

    # Define the topological structure of the ML-MCTDH tree
    graph, root = {
        'ROOT': ['ELECs', 'I0s'],
        'ELECs': ['ELEC', "ELEC'"],
        'I0s': ['I0', "I0'"],
    }, 'ROOT'
    root = Tensor.generate(graph, root)

    # Define the detailed parameters for the MC-MCTDH tree
    solver = MultiLayer(root, sbm.h_list, f_list=sbm.f_list, use_str_name=True)
    bond_dict = {}
    # Leaves
    for s, i, t, j in root.linkage_visitor():
        if isinstance(t, Leaf):
            try:
                dim = sbm.dimensions[t.name]
            except KeyError:
                dim = sbm.dimensions[t.name[:-1]]
            bond_dict[(s, i, t, j)] = dim
            s_ax = s.axis
            p, p_ax = s[s_ax]
            bond_dict[(p, p_ax, s, s_ax)] = dim**2 if dim < 9 else 50
    solver.autocomplete(bond_dict, max_entangled=True)

    # Define the computation details
    solver.settings(cmf_steps=10,
                    ode_method='RK45',
                    ps_method='split-unite',
                    snd_order=snd)
    logging.info("Size of a wfn: {} complexes".format(len(root.vectorize())))

    # Do the imaginary time propogation
    inv_tem = 1 / 1000
    steps = 100
    for time, _ in solver.propagator(
            steps=steps,
            ode_inter=Quantity(inv_tem / steps / 2, unit='K-1').value_in_au,
            split=True,
            imaginary=True):
        t = 2 * Quantity(time).convert_to(unit='K-1').value
        z = solver.relative_partition_function
        kelvin = 'inf' if abs(t) < 1.e-14 else 1.0 / t
        logging.warning('Temperatue: {} K; ln(Z/Z_0): {}'.format(
            kelvin, np.log(z)))

    # Define the obersevable of interest
    projector = np.array([[0., 0.], [0., 1.]])
    op = [[[root[0][0][1][0], projector]]]

    # Do the real time propogation
    tp_list = []
    steps = 100
    root.is_normalized = True
    for time, _ in solver.propagator(steps=steps,
                                     ode_inter=Quantity(10 / steps,
                                                        'fs').value_in_au,
                                     split=True,
                                     imaginary=False):
        t = Quantity(time).convert_to(unit='fs').value
        p = solver.expection(op=op)
        logging.warning('Time: {:.2f} fs; P2: {}'.format(t, p))
        tp_list.append((t, p))
    return np.array(tp_list)
示例#9
0
def sbm_zt(including_bath=False, split=False, snd=False):
    sbm_para_dict = {
        'including_bath':
        including_bath,
        'e1':
        0.,
        'e2':
        Quantity(6500, 'cm-1').value_in_au,
        'v':
        Quantity(500, 'cm-1').value_in_au,
        "omega_list": [
            Quantity(2100, 'cm-1').value_in_au,
            Quantity(650, 'cm-1').value_in_au,
            Quantity(400, 'cm-1').value_in_au,
            Quantity(150, 'cm-1').value_in_au
        ],
        'lambda_list': ([Quantity(750, 'cm-1').value_in_au] * 4),
        'dim_list': [10, 14, 20, 30],
        'stop':
        Quantity(3 * 2250, 'cm-1').value_in_au,
        'n':
        32,
        'dim':
        30,
        'lambda_g':
        Quantity(2250, 'cm-1').value_in_au,
        'omega_g':
        Quantity(500, 'cm-1').value_in_au,
        'lambda_d':
        Quantity(1250, 'cm-1').value_in_au,
        'omega_d':
        Quantity(50, 'cm-1').value_in_au,
        'mu':
        Quantity(250, 'cm-1').value_in_au,
        'tau':
        Quantity(30, 'fs').value_in_au,
        't_d':
        Quantity(60, 'fs').value_in_au,
        'omega':
        Quantity(13000, 'cm-1').value_in_au,
    }
    sbm = SpinBosonModel(**sbm_para_dict)

    # Define the topological structure of the ML-MCTDH tree
    graph, root = sbm.autograph(n_branch=2)
    root = Tensor.generate(graph, root)

    # Define the detailed parameters for the MC-MCTDH tree
    solver = MultiLayer(root, sbm.h_list, f_list=sbm.f_list, use_str_name=True)
    bond_dict = {}
    # Leaves
    for s, i, t, j in root.linkage_visitor():
        if isinstance(t, Leaf):
            bond_dict[(s, i, t, j)] = sbm.dimensions[t.name]
    # ELEC part
    elec_r = root[0][0]
    for s, i, t, j in elec_r.linkage_visitor(leaf=False):
        raise NotImplementedError()
    # INNER part
    inner_r = root[1][0] if including_bath else root
    if including_bath:
        bond_dict[(root, 1, inner_r, 0)] = 60
    for s, i, t, j in inner_r.linkage_visitor(leaf=False):
        bond_dict[(s, i, t, j)] = 50
    # OUTER part
    if including_bath:
        outer_r = root[2][0]
        bond_dict[(root, 2, outer_r, 0)] = 20
        for s, i, t, j in root[2][0].linkage_visitor(leaf=False):
            bond_dict[(s, i, t, j)] = 10
    solver.autocomplete(bond_dict, max_entangled=False)

    # Define the computation details
    solver.settings(
        max_ode_steps=100,
        cmf_steps=(1 if split else 10),
        ode_method='RK45',
        ps_method='s',
        snd_order=snd,
    )
    root.is_normalized = True
    # Define the obersevable of interest
    projector = np.array([[0., 0.], [0., 1.]])
    op = [[[root[0][0], projector]]]
    t_p = []
    for time, _ in solver.propagator(
            steps=20,
            ode_inter=Quantity(0.2, 'fs').value_in_au,
            split=split,
            move_energy=True,
    ):
        t, p = (Quantity(time).convert_to(unit='fs').value,
                solver.expection(op=op))
        t_p.append((t, p))
        logging.warning('Time: {:.2f} fs, P2: {}'.format(t, p))
        if np.abs(p) > 0.5:
            break

    # Save the results
    msg = 'split' if split else 'origin'
    msg2 = 'snd' if snd else 'fst'
    np.savetxt('sbm-zt-{}-{}.dat'.format(msg, msg2), t_p)
示例#10
0
        Acting on 0-th index.
        """
        derivatives = self._diff_h() + self._diff_n()

        for k in range(self.k_max):
            derivatives.extend(self._diff_k(k))

        return derivatives


if __name__ == '__main__':
    from minitn.heom.noise import Drude
    from minitn.lib.units import Quantity

    # System
    e = Quantity(6500, 'cm-1').value_in_au
    v = Quantity(500, 'cm-1').value_in_au
    # Bath
    lambda_0 = Quantity(2000, 'cm-1').value_in_au  # reorganization energy
    omega_0 = Quantity(2000, 'cm-1').value_in_au  # vibrational frequency
    beta = Quantity(300, 'K').value_in_au  # temperature
    # Superparameters
    max_terms = 5  # (terms used in the expansion of the correlation function)
    max_tier = 10  # (number of possble values for each n_k in the extended rho)

    h = np.array([[0, v], [v, e]])

    op = np.array([[0, 0], [0, 1]])

    corr = Drude(lambda_0, omega_0, max_terms, beta)
    heom = Hierachy([max_tier] * max_terms, h, op, corr)
示例#11
0
def sbm_zt(including_bath=False, split=False, snd=False):
    omega = 13000
    sbm = SpinBosonModel(
        including_bath=including_bath,
        e1=0.,
        e2=Quantity(6500, 'cm-1').value_in_au,
        v=Quantity(500, 'cm-1').value_in_au,
        omega_list=[
            Quantity(2100, 'cm-1').value_in_au,
            Quantity(650, 'cm-1').value_in_au,
            Quantity(400, 'cm-1').value_in_au,
            Quantity(150, 'cm-1').value_in_au
        ],
        lambda_list=([Quantity(750, 'cm-1').value_in_au] * 4),
        dim_list=[10, 14, 20, 30],
        stop=Quantity(3 * 2250, 'cm-1').value_in_au,
        n=32,
        dim=30,
        lambda_g=Quantity(2250, 'cm-1').value_in_au,
        omega_g=Quantity(500, 'cm-1').value_in_au,
        lambda_d=Quantity(1250, 'cm-1').value_in_au,
        omega_d=Quantity(50, 'cm-1').value_in_au,
        mu=Quantity(250, 'cm-1').value_in_au,
        tau=Quantity(30, 'fs').value_in_au,
        t_d=Quantity(60, 'fs').value_in_au,
        omega=Quantity(omega, 'cm-1').value_in_au,
    )

    # Define the topological structure of the ML-MCTDH tree
    graph, root = sbm.autograph(n_branch=2)
    root = Tensor.generate(graph, root)

    # Define the detailed parameters for the MC-MCTDH tree
    solver = MultiLayer(root, sbm.h_list, f_list=sbm.f_list, use_str_name=True)
    bond_dict = {}
    # Leaves
    for s, i, t, j in root.linkage_visitor():
        if isinstance(t, Leaf):
            bond_dict[(s, i, t, j)] = sbm.dimensions[t.name]
    # ELEC part
    elec_r = root[0][0]
    for s, i, t, j in elec_r.linkage_visitor(leaf=False):
        raise NotImplementedError()
    # INNER part
    inner_r = root[1][0] if including_bath else root
    if including_bath:
        bond_dict[(root, 1, inner_r, 0)] = 60
    for s, i, t, j in inner_r.linkage_visitor(leaf=False):
        bond_dict[(s, i, t, j)] = 50
    # OUTER part
    if including_bath:
        outer_r = root[2][0]
        bond_dict[(root, 2, outer_r, 0)] = 20
        for s, i, t, j in root[2][0].linkage_visitor(leaf=False):
            bond_dict[(s, i, t, j)] = 10
    solver.autocomplete(bond_dict, max_entangled=False)

    # Define the computation details
    solver.settings(
        max_ode_steps=100,
        cmf_steps=(1 if split else 10),
        ode_method='RK45',
        ps_method='s',
        snd_order=snd,
    )
    print("Size of a wfn: {} complexes".format(len(root.vectorize())))
    root.is_normalized = True
    # Define the obersevable of interest
    projector = np.array([[0., 0.], [0., 1.]])
    op = [[[root[0][0], projector]]]
    t_p = []
    for time, _ in solver.propagator(
            steps=20,
            ode_inter=Quantity(0.2, 'fs').value_in_au,
            split=split,
            move_energy=True,
    ):
        t, p = (Quantity(time).convert_to(unit='fs').value,
                solver.expection(op=op))
        t_p.append((t, p))
        logging.warning('Time: {:.2f} fs, P2: {}'.format(t, p))
        if np.abs(p) > 0.5:
            break
    return np.array(t_p)
示例#12
0
from __future__ import absolute_import, division, print_function

from builtins import filter, map, range, zip
from minitn.heom.corr import Drude

from minitn.heom.network import tensor_train_template
from minitn.heom.propagate import MultiLayer
from minitn.lib.backend import DTYPE, np
from minitn.lib.logging import Logger
from minitn.lib.tools import huffman_tree
from minitn.lib.units import Quantity
from minitn.models.sbm import SBM
from minitn.tensor import Leaf, Tensor

# System: pure dephasing
e = Quantity(5000, 'cm-1').value_in_au
v = Quantity(500, 'cm-1').value_in_au
max_tier = 40
rank_heom = max_tier
rank_wfn = max_tier
beta = Quantity(1 / 300, 'K-1').value_in_au
prefix = 'drude_100_300K_t{}_'.format(max_tier)


ph_parameters = [
    #(Quantity(400, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    #(Quantity(800, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    #(Quantity(1200, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
    #(Quantity(1600, 'cm-1').value_in_au, Quantity(500, 'cm-1').value_in_au),
]
dof = len(ph_parameters)
示例#13
0
def ml(dof, e, v, eta, cutoff, scale=5, loc=None, steps=2000, ode_inter=0.1):
    f_ = 'dof{}-eta{}.log'.format(dof, eta)
    logger = Logger(filename=f_).logger

    # define parameters
    e = Quantity(e, 'cm-1').value_in_au
    v = Quantity(v, 'cm-1').value_in_au
    eta = Quantity(eta, 'cm-1').value_in_au
    omega0 = Quantity(cutoff, 'cm-1').value_in_au
    sys_hamiltonian = np.array([[-e / 2.0, v], [v, e / 2.0]], dtype=DTYPE)
    projector = np.array([[0.0, 0.0], [0.0, 1.0]],
                         dtype=DTYPE)  # S in H_SB = S x B

    primitive_dim = 100
    spf_dim = 20

    # Spectrum function
    def spec_func(omega):
        if 0 < omega < omega0:
            return eta
        else:
            return 0.0

    # Define all Leaf tensors and hamiltonian we need
    h_list = []
    sys_leaf = Leaf(name='sys0')
    h_list.append([(sys_leaf, -1.0j * sys_hamiltonian)])

    ph_parameters = linear_discretization(spec_func, omega0, dof)
    if loc is not None:
        adj_pair = (ph_parameters[loc][0], ph_parameters[loc][1] * scale)
        ph_parameters[loc] = adj_pair
    leaves = []
    for n, (omega, g) in enumerate(ph_parameters, 1):
        ph = Phonon(primitive_dim, omega)
        ph_leaf = Leaf(name='ph{}'.format(n))
        leaves.append(ph_leaf)
        # hamiltonian ph part
        h_list.append([(ph_leaf, -1.0j * ph.hamiltonian)])
        # e-ph part
        op = ph.annihilation_operator + ph.creation_operator
        h_list.append([(ph_leaf, g * op), (sys_leaf, -1.0j * projector)])

    def ph_spf(n=0):
        n += 1
        return Tensor(name='spf{}'.format(n), axis=0)

    graph, root = huffman_tree(leaves, obj_new=ph_spf, n_branch=2)
    try:
        graph[root].insert(0, sys_leaf)
    except KeyError:
        ph_leaf = root
        root = Tensor()
        graph[root] = [sys_leaf, ph_leaf]
    finally:
        root.name = 'wfn'
        root.axis = None

    print(graph)
    stack = [root]
    while stack:
        parent = stack.pop()
        for child in graph[parent]:
            parent.link_to(parent.order, child, 0)
            if child in graph:
                stack.append(child)

    # Define the detailed parameters for the ML-MCTDH tree
    solver = MultiLayer(root, h_list)
    bond_dict = {}
    # Leaves
    for s, i, t, j in root.linkage_visitor():
        if t.name.startswith('sys'):
            bond_dict[(s, i, t, j)] = 2
        else:
            if isinstance(t, Leaf):
                bond_dict[(s, i, t, j)] = primitive_dim
            else:
                bond_dict[(s, i, t, j)] = spf_dim
    solver.autocomplete(bond_dict)
    # set initial root array
    a, b = 1.0, 1.0
    init_proj = np.array([[a, 0.0], [b, 0.0]]) / np.sqrt(a**2 + b**2)
    root_array = Tensor.partial_product(root.array, 0, init_proj, 1)
    root.set_array(root_array)

    # Define the computation details
    solver.ode_method = 'RK45'
    solver.snd_order = True
    solver.cmf_steps = 1
    root.is_normalized = True
    # Define the obersevable of interest
    logger.info('''# time/fs    rho00  rho01  rho10  rho11''')
    for time, _ in solver.propagator(
            steps=steps,
            ode_inter=Quantity(ode_inter, 'fs').value_in_au,
            split=True,
    ):
        t = Quantity(time).convert_to(unit='fs').value
        for tensor in root.visitor(axis=None):
            tensor.reset()
        rho = root.partial_env(0, proper=False)
        for tensor in root.visitor(axis=None):
            tensor.reset()
        flat_data = [t] + list(np.reshape(rho, -1))
        logger.info('{}    {}  {}  {}  {}'.format(*flat_data))