Пример #1
0
def _hamiltonian_expected(cells, periodic, sites_per_cell,
                          freedom):
    """Expected Hamiltonian for the simple 1D lattice model"""
    # Within the cell, adjacent sites hop to each other, and it's always
    # non-periodic.
    cell = qutip.qdiags([-np.ones(sites_per_cell - 1)]*2, [1, -1])
    # To hop to the cell one to the left, then you have to drop from the lowest
    # in-cell location to the highest in the other.
    hop_left = qutip.qdiags(np.ones(cells - 1), 1)
    if periodic and cells > 2:
        # If cells <= 2 then all cells border each other anyway.
        hop_left += qutip.qdiags([1], -(cells - 1))
    drop_site = -qutip.projection(sites_per_cell, sites_per_cell-1, 0)
    if np.prod(freedom) != 1:
        # Degenerate degrees of freedom per lattice site are unchanged.
        identity = qutip.qeye(freedom)
        cell = qutip.tensor(cell, identity)
        drop_site = qutip.tensor(drop_site, identity)
    out = (qutip.tensor(qutip.qeye(cells), cell)
           + qutip.tensor(hop_left, drop_site)
           + qutip.tensor(hop_left, drop_site).dag())
    # Contract scalar spaces.
    dims = [x for x in [cells, sites_per_cell] + freedom
            if x != 1]
    dims = dims or [1]
    out.dims = [dims, dims]
    return out
Пример #2
0
    def du_dphi(self, time: float, phase: float) -> qutip.Qobj:
        """du_dphi(time: float in s, phase: float in rad) -> operator

        Get the matrix form of the partial derivative of the time-evolution
        operator with respect to phase."""
        self.__update_if_required(time, phase)
        eg = -1j*self.__eg_pre*self.__sin[:self.__off_diag_len]*self.__phase_tot
        return qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\
               + qutip.tensor(_proj_ge, qutip.qdiags(-np.conj(eg), self.order))
def split_diagonal_hamiltonian(H0, n_blocks):
    """Split a diagonal Hamiltonian into blocks"""
    diag_data = H0.diag()
    assert (H0 - qdiags(diag_data, 0)).data.nnz == 0
    res = []
    for i in range(n_blocks):
        res.append(diag_data.copy())
        for j in range(n_blocks):
            if i == j:
                continue
            res[-1][split_list(range(len(diag_data)), n_blocks)[j]] = 0
        res[-1] = qdiags(res[-1], 0)
    return res
Пример #4
0
def test_simdiag_degen():
    N = 10
    U = qutip.rand_unitary(N)
    commuting_matrices = [
        U * qutip.qdiags([0, 0, 0, 1, 1, 1, 2, 2, 3, 4], 0) * U.dag(),
        U * qutip.qdiags([0, 0, 0, 1, 2, 2, 2, 2, 2, 2], 0) * U.dag(),
        U * qutip.qdiags([0, 0, 2, 1, 1, 2, 2, 3, 3, 4], 0) * U.dag(),
    ]
    all_evals, evecs = qutip.simdiag(commuting_matrices)

    for matrix, evals in zip(commuting_matrices, all_evals):
        for eval, evec in zip(evals, evecs):
            np.testing.assert_allclose((matrix * evec).full(),
                                       (evec * eval).full())
Пример #5
0
def _crow_lattice(coupling,
                  phase_delay,
                  cells=4,
                  boundary="periodic",
                  sites_per_cell=1,
                  freedom=[2]):
    r"""
    Return a `qutip.Lattice1d` of a "Coupled Resonator Optical Waveguide"
    (CROW) with the given coupling strength and phase delay.

    See for example: https://www.doi.org/10.1103/PhysRevB.99.224201
    where `coupling` is $J$ and `phase_delay` is $\eta$.
    """
    cell_hamiltonian = coupling * np.sin(phase_delay) * qutip.sigmax()
    phase_term = np.exp(1j * phase_delay)
    hopping = [
        0.5 * coupling *
        qutip.qdiags([phase_term, phase_term.conj()], 0),
        0.5 * coupling * qutip.sigmax(),
    ]
    return qutip.Lattice1d(num_cell=cells,
                           boundary=boundary,
                           cell_num_site=sites_per_cell,
                           cell_site_dof=freedom,
                           Hamiltonian_of_cell=cell_hamiltonian,
                           inter_hop=hopping)
Пример #6
0
    def du_dt(self, time: float, phase: float) -> qutip.Qobj:
        """du_dt(time : float in s, phase : float in rad) -> operator

        Get the matrix form of the partial derivative of the time-evolution
        operator with respect to time."""
        self.__update_if_required(time, phase)
        ee = self.__ee_du_dt_pre * self.__phase_time * self.__sin
        eg = 0.5 * self.__eg_pre * self.__phase_tot * (\
                self.__rabi_mod * self.__cos - 1j * self.detuning * self.__sin\
             )[:self.__off_diag_len]
        self.__diag[self.__const_ind[0]:self.__const_ind[1]] = 0.0
        self.__diag[self.__ee_ind[0]:self.__ee_ind[1]] =\
            ee[:self.__ee_ind[1] - self.__ee_ind[0]]
        self.__diag[self.__gg_ind[0]:self.__gg_ind[1]] =\
            np.conj(ee[:self.__gg_ind[1] - self.__gg_ind[0]])
        return qutip.tensor(_proj_ee, qutip.qdiags(self.__diag[0:self.ns], 0))\
               + qutip.tensor(_proj_gg, qutip.qdiags(self.__diag[self.ns:], 0))\
               + qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\
               + qutip.tensor(_proj_ge, qutip.qdiags(-np.conj(eg), self.order))
Пример #7
0
    def u(self, time: float, phase: float) -> qutip.Qobj:
        """u(time: float in s, phase: float in rad) -> operator

        Get the matrix form of the time-evolution operator corresponding to this
        transition.  The result is ordered such that it should be applied to a
        vector [|e0>, |e1>, ..., |g0>, |g1>, ...]."""
        self.__update_if_required(time, phase)
        ee = self.__cos + 1j * self.detuning * self.__sin / self.__rabi_mod
        ee = ee * self.__phase_time
        self.__diag[self.__const_ind[0]:self.__const_ind[1]] = 1.0
        self.__diag[self.__ee_ind[0]:self.__ee_ind[1]] =\
            ee[:self.__ee_ind[1] - self.__ee_ind[0]]
        self.__diag[self.__gg_ind[0]:self.__gg_ind[1]] =\
            np.conj(ee[:self.__gg_ind[1] - self.__gg_ind[0]])
        eg = self.__sin[:self.__off_diag_len] * self.__eg_pre * self.__phase_tot
        ge = -np.conj(eg)
        return   qutip.tensor(_proj_ee, qutip.qdiags(self.__diag[:self.ns], 0))\
               + qutip.tensor(_proj_gg, qutip.qdiags(self.__diag[self.ns:], 0))\
               + qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\
               + qutip.tensor(_proj_ge, qutip.qdiags(ge, self.order))
Пример #8
0
def test_simdiag_degen_large():
    N = 20
    U = qutip.rand_unitary(N)
    commuting_matrices = [
        U * qutip.qdiags(np.random.randint(0, 3, N), 0) * U.dag()
        for _ in range(5)
    ]
    all_evals, evecs = qutip.simdiag(commuting_matrices, tol=1e-12)

    for matrix, evals in zip(commuting_matrices, all_evals):
        for eval, evec in zip(evals, evecs):
            np.testing.assert_allclose((matrix * evec).full(),
                                       (evec * eval).full())
Пример #9
0
def test_simdiag(num_mat):
    N = 10

    U = qutip.rand_unitary(N)
    commuting_matrices = [
        U * qutip.qdiags(np.random.rand(N), 0) * U.dag()
        for _ in range(num_mat)
    ]
    all_evals, evecs = qutip.simdiag(commuting_matrices)

    for matrix, evals in zip(commuting_matrices, all_evals):
        for eval, evec in zip(evals, evecs):
            assert matrix * evec == evec * eval
Пример #10
0
def test_simdiag_no_evals(num_mat):
    N = 10

    U = qutip.rand_unitary(N)
    commuting_matrices = [
        U * qutip.qdiags(np.random.rand(N), 0) * U.dag()
        for _ in range(num_mat)
    ]
    evecs = qutip.simdiag(commuting_matrices, evals=False)

    for matrix in commuting_matrices:
        for evec in evecs:
            Mvec = matrix * evec
            eval = Mvec.norm() / evec.norm()
            assert matrix * evec == evec * eval
Пример #11
0
def build_pot_op_dft(dim, sym_op):
    N = 2 * dim - 1  # Number of points where the potential is evaluated. The reasoning behind this is that the exponential operator exp(i*n*x) is nonzero in our subspace for 2*dim-1 different values of n. Thus to characterize all frequencies we need 2*dim-1 different points.
    x = linspace(0, 2 * pi * (N - 1) / N, N)
    x[x > pi] = x[x > pi] - 2 * pi
    u = lambdify(sym_op.free_symbols, sym_op)(x)
    u_fft = rfft(u)
    E_n = qdiags(ones(dim - 1), -1)  # Operator representation of exp(i*n*x)
    E_1 = E_n  # Operator representing exp(i*x)
    for n in range(len(u_fft)):
        if n == 0:
            op = u_fft[n] * qeye(dim)
        else:
            op += u_fft[n] * E_n
            op += conj(u_fft[n]) * E_n.dag()
            E_n *= E_1
    return op / N
Пример #12
0
    def test_fixed_ssh(self):
        intra, inter = -0.5, -0.6
        cells = 5
        lattice = _ssh_lattice(intra, inter,
                               cells=cells, sites_per_cell=2, freedom=[1])
        Hin = qutip.Qobj([[0, intra], [intra, 0]])
        Ht = qutip.Qobj([[0, 0], [inter, 0]])
        D = qutip.qeye(cells)
        T = qutip.qdiags([np.ones(cells-1), [1]], [1, 1-cells])
        hopping = qutip.tensor(T, Ht)
        expected_hamiltonian = qutip.tensor(D, Hin) + hopping + hopping.dag()
        assert lattice.Hamiltonian() == expected_hamiltonian

        test_k, test_energies = lattice.get_dispersion()
        band = np.array([0.35297281, 0.89185772, 1.1, 0.89185772, 0.35297281])
        _assert_angles_close(test_k, _k_expected(cells), atol=1e-12)
        np.testing.assert_allclose(test_energies, np.array([-band, band]),
                                   atol=1e-8)
Пример #13
0
def hamiltonian_int_i(V):
    """
        This function gives the Hamiltonian of 2 interacting electrons with
        interaction strength V.

    Parameters
    ----------
    V: float
        The interaction strength between electrons in units of meV.

    Returns
    -------
    H_site: Qobj
        This returns a 9 x 9 matrix representing a single interaction
        between electrons.
    """
    V = float(V)
    return qdiags([
        V, V / sqrt(2), V / sqrt(5), V / sqrt(2), V, V / sqrt(2), V / sqrt(5),
        V / sqrt(2), V
    ], 0)
Пример #14
0
def quantize_potential_dft(U, xmin = None, xmax = None):
    if xmin == None:
        xmin = -np.pi*np.ones(U.ndim)
    if xmax == None:
        xmax = np.pi*np.ones(U.ndim)
    UF = np.fft.fftn(U)
    N = U.shape
    s = [2*np.pi*(1-1/Nn)/(xmaxn-xminn) for Nn,xmaxn,xminn in zip(N,xmax,xmin)]
    theta = [-sn*xminn for sn,xminn in zip(s,xmin)]
    Nq = [int((Nn-1)/2) if Nn%2 else int(Nn/2) for Nn in N]
    Ep = []
    for D in range(len(Nq)):
        Ep.append(qt.tensor([np.exp(1j*thetan)*qt.qdiags(np.ones(2*Nqn),-1)
                        if D==d
                        else qt.qeye(2*Nqn+1)
                        for d, (thetan,Nqn) in enumerate(zip(theta,Nq))]))
    I = qt.tensor([qt.qeye(2*Nqn +1) for Nqn in Nq])
    ops = [I for i in range(len(Nq))]
    k_last = np.zeros(np.shape(Nq), dtype=int)
    out = 0
    for k in np.ndindex(*[Nqn+1 for Nqn in Nq]):
        k_diff = np.array(list(k))-k_last
        for d in range(len(Nq)):
            if not k_diff[d] == 0:
                if k[d] == 0:
                    ops[d] = I
                else:
                    ops[d] = Ep[d]*ops[d]
                    if k[d] == N[d]/2:
                        ops[d] = ops[d] / 2 # Because otherwise nyquist freq is counted double
        pm_iter = [[1] if ki == 0 else [1,-1] for ki in k]
        for dir in itertools.product(*pm_iter):
            ck = UF[tuple(np.array(list(dir))*k)]/np.prod(N)
            out += ck*np.prod([ops[d] if sgn == 1 else ops[d].dag() for d,sgn in enumerate(dir)])
        k_last = np.array(list(k))
    return out, Nq
Пример #15
0
def build_sin_charge(dim):
    op = 1j * (qdiags(ones(dim - 1), 1) - qdiags(ones(dim - 1), -1)) / 2
    return op
Пример #16
0
 def test_binary(self, p):
     dm = qutip.qdiags([p, 1 - p], 0)
     expected = 0 if p in [0, 1
                           ] else p * np.log2(p) + (1 - p) * np.log2(1 - p)
     assert abs(-qutip.entropy_vn(dm, 2) - expected) < 1e-12
Пример #17
0
def makeBlochOperators(gsEnergies,
                       esEnergies,
                       osc_strengths=1,
                       T1_opt=-1,
                       br_ratio=0,
                       decay_rates=None,
                       coh_decay_rates=0):
    """Construct a parameterised Hamiltonian and collapse operators for driven
    transitions between a ground state manifold with energy splittings
    @gsEnergies and excited state splittings. Returns in the format needed by
    the setAtomParameters function (H0, H1, Hlst)

    @esEnergies. Ground and excited states are coupled by a Hamiltonian H1 according to the values
    Args:
        osc_strengths: A detuning between ground and excited states is given y the hamiltonian H2.
        T1_opt: Decays from the excited state manifold to ground occur at the rate 1/T1_opt.
        br_ratio: Ratios for second order transitions to make osc_strengths (if not given explicity)

    Returns: H0, (Esym,H1), Hlst, c_opL
    """
    Ngs = len(gsEnergies)
    Nes = len(esEnergies)
    Nstates = Ngs + Nes
    gsL = [q.basis(Nstates, n) for n in range(Ngs)]
    esL = [q.basis(Nstates, Ngs + n) for n in range(Nes)]
    H1 = None

    try:
        Nbrs = len(br_ratio)
    except TypeError:
        br_ratio = [k * br_ratio**k for k in range(4)]
        Nbrs = len(br_ratio)
    if not np.iterable(osc_strengths):
        strngth = osc_strengths
        osc_strengths = np.zeros((Ngs, Nes), dtype='O')
        osc_strengths[np.diag_indices(min(Ngs, Nes))] = 1.
        for k in range(Ngs):
            for i in range(Nes):
                df = int(abs(k - i))
                if df == 0:  # Like-to-like transition
                    osc_strengths[k, i] = strngth
                if df < len(br_ratio) and df > 0:  # Second order or further...
                    osc_strengths[k, i] = strngth * br_ratio[df]
    # --------------------------------------------------------
    # Add field interaction operators (oscillator strengths)
    Esym = sm.symbols('Ef', complex=True)
    for k, es in enumerate(esL):
        for i, gs in enumerate(gsL):
            term = Esym * toDense(
                es * gs.dag()) + sm.conjugate(Esym) * toDense(gs * es.dag())
            if H1 is None:
                H1 = 0.5 * sm.sqrt(osc_strengths[i, k]) * term
            else:
                H1 += 0.5 * sm.sqrt(osc_strengths[i, k]) * term

    # ----------------------------------------------
    # H0 Evolution
    H0 = q.zero_ket(Nstates) * q.zero_ket(Nstates).dag()
    Hdet = H0.copy()
    H0 = toDense(H0)
    for freq, ket in zip(gsEnergies, gsL):
        H0 += 2 * pi * -freq * toDense(ket * ket.dag())
    for freq, ket in zip(esEnergies, esL):
        H0 += 2 * pi * -freq * toDense(ket * ket.dag())

    Hdet = toDense(
        q.qdiags(np.hstack([np.zeros(Ngs), np.ones(Nes)]), offsets=0))

    # -------------------------------------------------
    # Decay. If decay_rate is not given, assume it scales
    #    with oscillator strength. Otherwise, T1_opt does nothing
    gamma = 1. / T1_opt
    if decay_rates is None:
        decay_rates = [[gamma * osc_strengths[l][k] for k in range(len(esL))]
                       for l in range(len(gsL))]

    c_opL = []
    for k, es in enumerate(esL):
        for i, gs in enumerate(gsL):
            #c_opL.append(sm.sqrt(gamma*osc_strengths[l][k]) * toDense(gs * es.dag()))
            c_opL.append(sm.sqrt(decay_rates[i][k]) * toDense(gs * es.dag()))

    # T2s
    if coh_decay_rates != 0:
        if not isiterable(coh_decay_rates):
            rate = coh_decay_rates
            coh_decay_rates = [[rate for k in range(len(esL))]
                               for i in range(len(gsL))]
        for k, es in enumerate(esL):
            for i, gs in enumerate(gsL):
                c_opL.append(
                    sm.sqrt(coh_decay_rates[i][k]) *
                    toDense(1j * gs * es.dag() - 1j * es * gs.dag()))

    # An attempt to compensate for 'edge' transitions having different rates
    if T1_opt != -1 and 0:
        gamma_tot = 1. / T1_opt  # = gamma_1 + 2*br*gamma_1
        # gamma_tot =
        gamma_1 = gamma_tot / (1 + 2 * br_ratio)
        gamma_2 = br_ratio * gamma_1
        # Make collapse operators
        c_opL = [
            sm.sqrt(gamma_1) * toDense(gs * es.dag())
            for gs, es in zip(gsL, esL)
        ]
        c_opL += [
            sm.sqrt(gamma_2) * toDense(gs * es.dag())
            for gs, es in zip(gsL[1:], esL)
        ]
        c_opL += [
            sm.sqrt(gamma_2) * toDense(gs * es.dag())
            for gs, es in zip(gsL, esL[1:])
        ]
        print("N c_ops: {}".format(len(c_opL)))
        print(c_opL)

    delt = sm.symbols('Delta', real=True)
    # Hlst=[
    return H0, (Esym, H1), (delt, Hdet), c_opL
Пример #18
0
 def test_known_cases(self):
     r1 = qdiags(np.array([0.5, 0.5, 0, 0]), 0)
     r2 = qdiags(np.array([0, 0, 0.5, 0.5]), 0)
     assert hilbert_dist(r1, r2) == pytest.approx(1, abs=1e-6)
Пример #19
0
def build_cos_charge(dim):
    op = (qdiags(ones(dim - 1), 1) + qdiags(ones(dim - 1), -1)) / 2
    return op
Пример #20
0
_l_adc_system = 0.1 * (2*qutip.tensor(_sm, _sp.dag())
                       - qutip.tensor(_project_0, _si)
                       - qutip.tensor(_si, _project_0.dag()))
_l_adc_controls = [1j * (qutip.tensor(_si, _sz) - qutip.tensor(_sz, _si)),
                   1j * (qutip.tensor(_si, _sx) - qutip.tensor(_sx, _si))]
_l_adc_kwargs = {'num_tslots': 10, 'evo_time': 5, 'init_pulse_type': 'LIN',
                 'max_iter': 200, 'fid_err_targ': 1e-1, 'gen_stats': True}
l_adc = _System(system=_l_adc_system,
                controls=_l_adc_controls,
                initial=qutip.identity([2, 2]),
                target=hadamard_transform(2),
                kwargs=_l_adc_kwargs)

# Two coupled oscillators with symplectic dynamics.
_g1, _g2 = 1.0, 0.2
_A_rotate = qutip.qdiags([[1, 1, 0, 0]], [0])
_A_squeeze = 0.4 * qutip.qdiags([[1, -1, 0, 0]], [0])
_A_target = qutip.qdiags([[1, 1], [1, 1]], [2, -2])
_Omega = qutip.Qobj(qutip.control.symplectic.calc_omega(2))
_sympl_system = qutip.qdiags([[1, 1, 1, 1], [_g1, _g2], [_g1, _g2]],
                             [0, 2, -2])
_sympl_target = (-0.5 * _A_target * _Omega * np.pi).expm()
_sympl_kwargs = {'num_tslots': 20, 'evo_time': 10, 'fid_err_targ': 1e-3,
                 'max_iter': 200, 'dyn_type': 'SYMPL',
                 'init_pulse_type': 'ZERO', 'gen_stats': True}
symplectic = _System(system=_sympl_system,
                     controls=[_A_rotate, _A_squeeze],
                     initial=qutip.identity(4),
                     target=_sympl_target,
                     kwargs=_sympl_kwargs)
Пример #21
0
def test_qdiags_type():
    "Operator CSR Type: qdiags"
    op = qdiags(np.sqrt(range(1, 4)), 1)
    assert_equal(isspmatrix_csr(op.data), True)
Пример #22
0
ini_coeff = [0, 1e-9, 0, 1e-9, 1, 0, 0, 0, 0]  # 11
ini_state = ini_coeff[0]*qt.tensor(ket(Nq,0), ket(Nq,0)) \
            + ini_coeff[1]*qt.tensor(ket(Nq,0), ket(Nq,1)) \
            + ini_coeff[2]*qt.tensor(ket(Nq,0), ket(Nq,2)) \
            + ini_coeff[3]*qt.tensor(ket(Nq,1), ket(Nq,0)) \
            + ini_coeff[4]*qt.tensor(ket(Nq,1), ket(Nq,1)) \
            + ini_coeff[5]*qt.tensor(ket(Nq,1), ket(Nq,2)) \
            + ini_coeff[6]*qt.tensor(ket(Nq,2), ket(Nq,0)) \
            + ini_coeff[7]*qt.tensor(ket(Nq,2), ket(Nq,1)) \
            + ini_coeff[8]*qt.tensor(ket(Nq,2), ket(Nq,2))

Iq1 = qt.qeye(Nq)
Hq1_lab = QQ.Hq1 * (2 * pi)
rot2 = Hq(Q2.Nq, 0, abs(Q2.anh * (2 * pi)))
q2Freqs = qt.qdiags(np.arange(0, Q2.Nq, 1), 0)
Hq2_t_ind = qt.tensor(Iq1, rot2)  #Hq2_rot(constant term)
Hq2_t_dep = qt.tensor(Iq1, q2Freqs)  #Hq2_rot(modulation term)
Hint = QQ.Hint12 * (2 * pi)
H_rot = [Hq1_lab + Hq2_t_ind + Hint, [Hq2_t_dep, MW_shaped]]
tgstart = 48
tgend = 48

for gt in range(tgstart, tgend + 1):

    pulsesystem = CZpulse(Q1, Q2, g, the_f=0.88, lambda2=0.13, gatetime=gt)
    tg = pulsesystem.tg
    t_list, adiabaticpulse = pulsesystem.netzeropulse()
    degeneracy_freq = pulsesystem.qFreq20

    args = {'mwamp': 1.0, 'shape': adiabaticpulse}
Пример #23
0
def test_qdiags_type():
    "Operator CSR Type: qdiags"
    op = qdiags(np.sqrt(range(1, 4)), 1)
    assert_equal(isspmatrix_csr(op.data), True)
Пример #24
0
    multiplied by a unit complex number.
    """
    vec = vec.flatten()
    nonzero = vec != 0
    if not np.any(nonzero):
        return vec
    return vec / vec[np.argmax(nonzero)]


# Random diagonal Hamiltonian.
_diagonal_dimension = 10
_diagonal_eigenvalues = np.sort(np.random.rand(_diagonal_dimension))
_diagonal_eigenstates = np.array([[0] * n + [1] + [0] *
                                  (_diagonal_dimension - n - 1)
                                  for n in range(_diagonal_dimension)])
_diagonal_hamiltonian = qutip.qdiags(_diagonal_eigenvalues, 0)

# Arbitrary known non-diagonal complex Hamiltonian.
_nondiagonal_hamiltonian = qutip.Qobj(
    np.array([[0.16252356, 0.27696416 + 0.0405202j, 0.19577420 + 0.07815636j],
              [0.27696416 - 0.0405202j, 0.45859633, 0.36222915 + 0.17372725j],
              [0.19577420 - 0.07815636j, 0.36222915 - 0.17372725j,
               0.44149665]]))
_nondiagonal_eigenvalues = np.array(
    [-0.022062710138316392, 0.08888141616526818, 0.995797833973048])
_nondiagonal_eigenstates = np.array(
    [[
        -0.737511505546763, 0.5270680510449308 - 0.29398599661318j,
        0.009793118179759598 + 0.3029065489313791j
    ],
     [