示例#1
0
def evolve_fqe_givens_unrestricted(wfn: Wavefunction,
                                   u: np.ndarray) -> Wavefunction:
    """Evolve a wavefunction by u generated from a 1-body Hamiltonian.

    Args:
        wfn: 2^{n} x 1 vector.
        u: (n x n) unitary matrix.

    Returns:
        New evolved wfn object.
    """
    rotations, diagonal = givens_decomposition_square(u.copy())
    # Iterate through each layer and time evolve by the appropriate
    # fermion operators
    for layer in rotations:
        for givens in layer:
            i, j, theta, phi = givens
            if not np.isclose(phi, 0):
                op = of.FermionOperator(((j, 1), (j, 0)), coefficient=-phi)
                wfn = wfn.time_evolve(1.0, op)
            if not np.isclose(theta, 0):
                op = of.FermionOperator(
                    ((i, 1),
                     (j, 0)), coefficient=-1j * theta) + of.FermionOperator(
                         ((j, 1), (i, 0)), coefficient=1j * theta)
                wfn = wfn.time_evolve(1.0, op)

    # evolve the last diagonal phases
    for idx, final_phase in enumerate(diagonal):
        if not np.isclose(final_phase, 1.0):
            op = of.FermionOperator(((idx, 1), (idx, 0)),
                                    -np.angle(final_phase))
            wfn = wfn.time_evolve(1.0, op)

    return wfn
示例#2
0
def evolve_fqe_givens_sector(wfn: Wavefunction, u: np.ndarray,
                             sector='alpha') -> Wavefunction:
    """Evolve a wavefunction by u generated from a 1-body Hamiltonian.

    Args:
        wfn: FQE Wavefunction on n-orbitals
        u: (n x n) unitary matrix.
        sector: Optional either 'alpha' or 'beta' indicating which sector
                to rotate
    Returns:
        New evolved wfn object.
    """
    if sector == 'alpha':
        sigma = 0
    elif sector == 'beta':
        sigma = 1
    else:
        raise ValueError("Bad section variable.  Either (alpha) or (beta)")

    if not np.isclose(u.shape[0], wfn.norb()):
        raise ValueError(
            "unitary is not specified for the correct number of orbitals")

    rotations, diagonal = givens_decomposition_square(u.copy())
    # Iterate through each layer and time evolve by the appropriate
    # fermion operators
    for layer in rotations:
        for givens in layer:
            i, j, theta, phi = givens
            if not np.isclose(phi, 0):
                op = of.FermionOperator(
                    ((2 * j + sigma, 1), (2 * j + sigma, 0)), coefficient=-phi)
                wfn = wfn.time_evolve(1.0, op)
            if not np.isclose(theta, 0):
                op = of.FermionOperator(((2 * i + sigma, 1),
                                         (2 * j + sigma, 0)),
                                        coefficient=-1j * theta) + \
                     of.FermionOperator(((2 * j + sigma, 1),
                                         (2 * i + sigma, 0)),
                                        coefficient=1j * theta)
                wfn = wfn.time_evolve(1.0, op)

    # evolve the last diagonal phases
    for idx, final_phase in enumerate(diagonal):
        if not np.isclose(final_phase, 1.0):
            op = of.FermionOperator(
                ((2 * idx + sigma, 1), (2 * idx + sigma, 0)),
                -np.angle(final_phase))
            wfn = wfn.time_evolve(1.0, op)

    return wfn
示例#3
0
def evolve_fqe_charge_charge_sector(wfn: Wavefunction,
                                    vij_mat: np.ndarray,
                                    sector='alpha',
                                    time=1) -> Wavefunction:
    r"""Utility for testing evolution of a full 2^{n} wavefunction via

    :math:`exp{-i time * \sum_{i,j}v_{i, j}n_{i, alpha}n_{j, beta}}.`

    Args:
        wfn: fqe_wf with sdim = n
        vij_mat: List[(n x n] matrices n is the spatial orbital rank
        time: evolution time.

    Returns:
        New evolved 2^{2 * n} x 1 vector
    """
    if sector == 'alpha':
        sigma = 0
    elif sector == 'beta':
        sigma = 1
    else:
        raise ValueError("Sector must be alpha or beta.")

    norbs = vij_mat.shape[0]
    for p, q in product(range(norbs), repeat=2):
        if np.isclose(vij_mat[p, q], 0):
            continue
        fop = of.FermionOperator(((2 * p + sigma, 1), (2 * p + sigma, 0),
                                  (2 * q + sigma, 1), (2 * q + sigma, 0)),
                                 coefficient=vij_mat[p, q])
        wfn = wfn.time_evolve(time, fop)
    return wfn
    def test_apply_individual_nbody_error(self):
        fop = FermionOperator('1^ 0')
        fop += FermionOperator('2^ 0')
        fop += FermionOperator('2^ 1')
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        wfn = Wavefunction([[2, 0, 2]], broken=['spin'])
        self.assertRaises(ValueError, wfn._apply_individual_nbody, hamil)
        self.assertRaises(ValueError, wfn._evolve_individual_nbody, 0.1, hamil)

        fop = FermionOperator('1^ 0')
        fop += FermionOperator('2^ 0')
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        self.assertRaises(ValueError, wfn._evolve_individual_nbody, 0.1, hamil)

        fop = FermionOperator('1^ 0', 1.0)
        fop += FermionOperator('0^ 1', 0.9)
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        self.assertRaises(ValueError, wfn._evolve_individual_nbody, 0.1, hamil)

        fop = FermionOperator('1^ 0^')
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        self.assertRaises(ValueError, wfn._apply_individual_nbody, hamil)
        self.assertRaises(ValueError, wfn._evolve_individual_nbody, 0.1, hamil)

        self.assertRaises(TypeError, wfn._evolve_individual_nbody, 0.1, 1)
    def test_apply_type_error(self):
        data = numpy.zeros((2, 2), dtype=numpy.complex128)
        wfn = Wavefunction([[2, 0, 2]], broken=['spin'])
        hamil = general_hamiltonian.General((data, ))
        hamil._conserve_number = False
        self.assertRaises(TypeError, wfn.apply, hamil)
        self.assertRaises(TypeError, wfn.time_evolve, 0.1, hamil)

        wfn = Wavefunction([[2, 0, 2]], broken=['number'])
        hamil = general_hamiltonian.General((data, ))
        self.assertRaises(TypeError, wfn.apply, hamil)
        self.assertRaises(TypeError, wfn.time_evolve, 0.1, hamil)

        wfn = Wavefunction([[2, 0, 2]])
        hamil = get_restricted_hamiltonian((data, ))
        self.assertRaises(ValueError, wfn.time_evolve, 0.1, hamil, True)
示例#6
0
    def test_lih_energy(self):
        """Checking total energy with LiH
        """
        eref = -8.877719570384043
        norb = 6
        nalpha = 2
        nbeta = 2
        nele = nalpha + nbeta
        h1e, h2e, lih_ground = build_lih_data.build_lih_data('energy')

        elec_hamil = general_hamiltonian.General((h1e, h2e))
        wfn = Wavefunction([[nele, nalpha - nbeta, norb]])
        wfn.set_wfn(strategy='from_data',
                    raw_data={(nele, nalpha - nbeta): lih_ground})

        ecalc = wfn.expectationValue(elec_hamil)
        self.assertAlmostEqual(eref, ecalc, places=8)
示例#7
0
    def test_apply_spinful_fermionop(self):
        """
        Make sure the spin-orbital reordering is working by comparing
        apply operation
        """
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        cirq_wf = to_cirq(wfn).reshape((-1, 1))

        op_to_apply = FermionOperator()
        test_state = copy.deepcopy(wfn)
        test_state.set_wfn('zero')
        for p, q, r, s in product(range(2), repeat=4):
            op = FermionOperator(
                ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)),
                coefficient=numpy.random.randn())
            op_to_apply += op + hermitian_conjugated(op)
            test_state += wfn.apply(op + hermitian_conjugated(op))

        opmat = get_sparse_operator(op_to_apply, n_qubits=4).toarray()
        new_state_cirq = opmat @ cirq_wf

        # this part is because we need to pass a normalized wavefunction
        norm_constant = new_state_cirq.conj().T @ new_state_cirq
        new_state_cirq /= numpy.sqrt(norm_constant)
        new_state_wfn = from_cirq(new_state_cirq.flatten(), thresh=1.0E-12)
        new_state_wfn.scale(numpy.sqrt(norm_constant))

        self.assertTrue(
            numpy.allclose(test_state.get_coeff((2, 0)),
                           new_state_wfn.get_coeff((2, 0))))
示例#8
0
    def test_evolve_spinful_fermionop(self):
        """
        Make sure the spin-orbital reordering is working by comparing
        time evolution
        """
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        cirq_wf = to_cirq(wfn).reshape((-1, 1))

        op_to_apply = FermionOperator()
        for p, q, r, s in product(range(2), repeat=4):
            op = FermionOperator(
                ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)),
                coefficient=numpy.random.randn())
            op_to_apply += op + hermitian_conjugated(op)

        opmat = get_sparse_operator(op_to_apply, n_qubits=4).toarray()
        dt = 0.765
        new_state_cirq = scipy.linalg.expm(-1j * dt * opmat) @ cirq_wf
        new_state_wfn = from_cirq(new_state_cirq.flatten(), thresh=1.0E-12)
        test_state = wfn.time_evolve(dt, op_to_apply)
        self.assertTrue(
            numpy.allclose(test_state.get_coeff((2, 0)),
                           new_state_wfn.get_coeff((2, 0))))
 def test_general_exceptions(self):
     """Test general method exceptions
     """
     test1 = Wavefunction(param=[[2, 0, 4]])
     test2 = Wavefunction(param=[[4, -4, 8]])
     test1.set_wfn(strategy='ones')
     test2.set_wfn(strategy='ones')
     self.assertRaises(ValueError, test1.ax_plus_y, 1.0, test2)
     self.assertRaises(ValueError, test1.__add__, test2)
     self.assertRaises(ValueError, test1.__sub__, test2)
     self.assertRaises(ValueError, test1.set_wfn, strategy='from_data')
示例#10
0
    def test_lih_dipole(self):
        """Calculate the LiH dipole
        """
        norb = 6
        nalpha = 2
        nbeta = 2
        nele = nalpha + nbeta
        au2debye = 2.5417464157449032

        dip_ref, dip_mat, lih_ground = build_lih_data.build_lih_data('dipole')

        wfn = Wavefunction([[nele, nalpha - nbeta, norb]])
        wfn.set_wfn(strategy='from_data',
                    raw_data={(nele, nalpha - nbeta): lih_ground})

        hwfn_x = wfn._apply_array(tuple([dip_mat[0]]), e_0=0. + 0.j)
        hwfn_y = wfn._apply_array(tuple([dip_mat[1]]), e_0=0. + 0.j)
        hwfn_z = wfn._apply_array(tuple([dip_mat[2]]), e_0=0. + 0.j)
        calc_dip = numpy.array([fqe.vdot(wfn, hwfn_x).real, \
                              fqe.vdot(wfn, hwfn_y).real, \
                              fqe.vdot(wfn, hwfn_z).real])*au2debye
        for card in range(3):
            with self.subTest(dip=card):
                err = abs(calc_dip[card] - dip_ref[card])
                self.assertTrue(err < 1.e-5)
示例#11
0
    def test_save_read(self):
        """Check that the wavefunction can be properly archived and
        retieved
        """
        numpy.random.seed(seed=409)
        wfn = get_number_conserving_wavefunction(3, 3)
        wfn.set_wfn(strategy='random')
        wfn.save('test_save_read')
        read_wfn = Wavefunction()
        read_wfn.read('test_save_read')
        for key in read_wfn.sectors():
            self.assertTrue(
                numpy.allclose(read_wfn._civec[key].coeff,
                               wfn._civec[key].coeff))
        self.assertEqual(read_wfn._symmetry_map, wfn._symmetry_map)
        self.assertEqual(read_wfn._conserved, wfn._conserved)
        self.assertEqual(read_wfn._conserve_spin, wfn._conserve_spin)
        self.assertEqual(read_wfn._conserve_number, wfn._conserve_number)
        self.assertEqual(read_wfn._norb, wfn._norb)

        os.remove('test_save_read')

        wfn = get_spin_conserving_wavefunction(2, 6)
        wfn.set_wfn(strategy='random')
        wfn.save('test_save_read')
        read_wfn = Wavefunction()
        read_wfn.read('test_save_read')
        for key in read_wfn.sectors():
            self.assertTrue(
                numpy.allclose(read_wfn._civec[key].coeff,
                               wfn._civec[key].coeff))
        self.assertEqual(read_wfn._symmetry_map, wfn._symmetry_map)
        self.assertEqual(read_wfn._conserved, wfn._conserved)
        self.assertEqual(read_wfn._conserve_spin, wfn._conserve_spin)
        self.assertEqual(read_wfn._conserve_number, wfn._conserve_number)
        self.assertEqual(read_wfn._norb, wfn._norb)

        os.remove('test_save_read')
示例#12
0
    def test_apply_number(self):
        norb = 4
        test = numpy.random.rand(norb, norb)
        diag = numpy.random.rand(norb * 2)
        diag2 = copy.deepcopy(diag)
        e_0 = 0
        for i in range(norb):
            e_0 += diag[i + norb]
            diag2[i + norb] = -diag[i + norb]
        hamil = diagonal_hamiltonian.Diagonal(diag2, e_0=e_0)
        hamil._conserve_number = False
        wfn = Wavefunction([[4, 2, norb]], broken=['number'])
        wfn.set_wfn(strategy='from_data', raw_data={(4, 2): test})
        out1 = wfn.apply(hamil)

        hamil = diagonal_hamiltonian.Diagonal(diag)
        wfn = Wavefunction([[4, 2, norb]])
        wfn.set_wfn(strategy='from_data', raw_data={(4, 2): test})
        out2 = wfn.apply(hamil)

        self.assertTrue(
            numpy.allclose(out1._civec[(4, 2)].coeff,
                           out2._civec[(4, 2)].coeff))
示例#13
0
def evolve_fqe_diagaonal_coulomb(wfn: Wavefunction, vij_mat: np.ndarray,
                                 time=1) -> Wavefunction:
    r"""Utility for testing evolution of a full 2^{n} wavefunction via

    :math:`exp{-i time * \sum_{i,j, sigma, tau}v_{i, j}n_{i\sigma}n_{j\tau}}.`

    Args:
        wfn: 2^{n} x 1 vector.
        vij_mat: List[(n//2 x n//2)] matrices
        time: evolution time.

    Returns:
        New evolved 2^{n} x 1 vector
    """
    dc_ham = DiagonalCoulomb(vij_mat)
    return wfn.time_evolve(time, dc_ham)
示例#14
0
    def test_lih_ops(self):
        """Check the value of the operators on LiH
        """
        norb = 6
        nalpha = 2
        nbeta = 2
        nele = nalpha + nbeta

        _, _, lih_ground = build_lih_data.build_lih_data('energy')

        wfn = Wavefunction([[nele, nalpha - nbeta, norb]])
        wfn.set_wfn(strategy='from_data',
                    raw_data={(nele, nalpha - nbeta): lih_ground})

        operator = S2Operator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 0. + 0.j)
        operator = SzOperator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 0. + 0.j)
        operator = TimeReversalOp()
        self.assertAlmostEqual(wfn.expectationValue(operator), 1. + 0.j)
        operator = NumberOperator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 4. + 0.j)
        self.assertAlmostEqual(wfn.expectationValue(operator, wfn), 4. + 0.j)
示例#15
0
    def test_apply_diagonal(self):
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')

        data = numpy.random.rand(2)
        hamil = diagonal_hamiltonian.Diagonal(data)
        out1 = wfn._apply_diagonal(hamil)

        fac = 0.5
        hamil = diagonal_hamiltonian.Diagonal(data, e_0=fac)
        out2 = wfn._apply_diagonal(hamil)
        out2.ax_plus_y(-fac, wfn)
        self.assertTrue((out1 - out2).norm() < 1.0e-8)
示例#16
0
 def test_rdm(self):
     """Check that the rdms will properly return the energy
     """
     wfn = Wavefunction(param=[[4, 0, 3]])
     work, energy = build_wfn.restricted_wfn_energy()
     wfn.set_wfn(strategy='from_data', raw_data={(4, 0): work})
     rdm1 = wfn.rdm('i^ j')
     rdm2 = wfn.rdm('i^ j^ k l')
     rdm3 = wfn.rdm('i^ j^ k^ l m n')
     rdm4 = wfn.rdm('i^ j^ k^ l^ m n o p')
     h1e, h2e, h3e, h4e = build_hamiltonian.build_restricted(3, full=False)
     expval = 0. + 0.j
     axes = [0, 1]
     expval += numpy.tensordot(h1e, rdm1, axes=(axes, axes))
     axes = [0, 1, 2, 3]
     expval += numpy.tensordot(h2e, rdm2, axes=(axes, axes))
     axes = [0, 1, 2, 3, 4, 5]
     expval += numpy.tensordot(h3e, rdm3, axes=(axes, axes))
     axes = [0, 1, 2, 3, 4, 5, 6, 7]
     expval += numpy.tensordot(h4e, rdm4, axes=(axes, axes))
     self.assertAlmostEqual(expval, energy)
示例#17
0
    def test_apply_nbody(self):
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')

        fac = 3.14
        fop = FermionOperator('1^ 1', fac)
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        out1 = wfn._apply_few_nbody(hamil)

        fop = FermionOperator('1 1^', fac)
        hamil = sparse_hamiltonian.SparseHamiltonian(fop)
        out2 = wfn._apply_few_nbody(hamil)
        out2.scale(-1.0)
        out2.ax_plus_y(fac, wfn)
        self.assertTrue((out1 - out2).norm() < 1.0e-8)
示例#18
0
    def test_wick(self):
        """Check that wick performs the proper restructuring of the
        density matrix given a string of indexes.
        """
        norb = 4
        nele = 4
        s_z = 0
        wfn = Wavefunction([[nele, s_z, norb]])
        numpy.random.seed(seed=1)
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        rdms = wfn._compute_rdm(4)
        out1 = wick.wick('k j^', list(rdms), True)
        two = numpy.eye(norb, dtype=out1.dtype) * 2.0
        self.assertRaises(ValueError, wick.wick, 'k0 j', list(rdms))
        self.assertTrue(numpy.allclose(two - out1.T, rdms[0]))

        self.assertRaises(ValueError, wick.wick, 'k^ l i^ j', list(rdms), True)
        out2 = wick.wick('k l i^ j^', list(rdms), True)

        h_1 = numpy.zeros_like(out1)
        for i in range(norb):
            h_1[:, :] += out2[:, i, :, i] / (norb * 2 - nele - 1)
        self.assertAlmostEqual(numpy.std(out1 + h_1), 0.)

        out2a = wick.wick('k l^ i^ j', list(rdms), True)
        self.assertAlmostEqual(out2a[2, 3, 0, 1], -rdms[1][0, 3, 2, 1])

        out3 = wick.wick('k l m i^ j^ n^', list(rdms), True)
        h_2 = numpy.zeros_like(out2)
        for i in range(norb):
            h_2[:, :, :, :] += out3[:, i, :, :, i, :] / (norb * 2 - nele - 2)
        self.assertAlmostEqual(numpy.std(out2 - h_2), 0.)

        out4 = wick.wick('k l m x i^ j^ n^ y^', list(rdms), True)
        h_3 = numpy.zeros_like(out3)
        for i in range(norb):
            h_3[:, :, :, :, :, :] += out4[:, i, :, :, :, i, :, :] / (norb * 2 -
                                                                     nele - 3)
        self.assertAlmostEqual(numpy.std(out3 + h_3), 0.)
示例#19
0
def evolve_fqe_charge_charge_unrestricted(wfn: Wavefunction,
                                          vij_mat: np.ndarray,
                                          time=1) -> Wavefunction:
    r"""Utility for testing evolution of a full 2^{n} wavefunction via

    :math:`exp{-i time * \sum_{i,j}v_{i, j}n_{i}n_{j}}.`

    Args:
        wfn: fqe_wf with sdim = n
        vij_mat: List[(n x n] matrices
        time: evolution time.

    Returns:
        New evolved 2^{n} x 1 vector
    """
    nso = vij_mat.shape[0]
    for p, q in product(range(nso), repeat=2):
        if np.isclose(vij_mat[p, q], 0):
            continue
        fop = of.FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                 coefficient=vij_mat[p, q])
        wfn = wfn.time_evolve(time, fop)
    return wfn
示例#20
0
 def test_general_functions(self):
     """Test general wavefunction members
     """
     test = Wavefunction(param=[[2, 0, 4]])
     test.set_wfn(strategy='ones')
     self.assertEqual(1. + 0.j, test[(4, 8)])
     test[(4, 8)] = 3.14 + 0.00159j
     self.assertEqual(3.14 + 0.00159j, test[(4, 8)])
     self.assertEqual(3.14 + 0.00159j, test.max_element())
     self.assertTrue(test.conserve_spin())
     test1 = Wavefunction(param=[[2, 0, 4]])
     test2 = Wavefunction(param=[[2, 0, 4]])
     test1.set_wfn(strategy='ones')
     test2.set_wfn(strategy='ones')
     work = test1 + test2
     ref = 2.0 * numpy.ones((4, 4), dtype=numpy.complex128)
     self.assertTrue(numpy.allclose(ref, work._civec[(2, 0)].coeff))
     work = test1 - test2
     ref = numpy.zeros((4, 4), dtype=numpy.complex128)
     self.assertTrue(numpy.allclose(ref, work._civec[(2, 0)].coeff))
示例#21
0
    def vbc(self,
            initial_wf: Wavefunction,
            opt_method: str = 'L-BFGS-B',
            opt_options=None,
            num_opt_var=None,
            v_reconstruct=False,
            generator_decomp=None,
            generator_rank=None):
        """The variational Brillouin condition method

        Solve for the 2-body residual and then variationally determine
        the step size.  This exact simulation cannot be implemented without
        Trotterization.  A proxy for the approximate evolution is the update_
        rank pameter which limites the rank of the residual.

        Args:
            initial_wf: initial wavefunction
            opt_method: scipy optimizer name
            num_opt_var: Number of optimization variables to consider
            v_reconstruct: use valdemoro reconstruction of 3-RDM to calculate
                           the residual
            generator_decomp: None, takagi, or svd
            generator_rank: number of generator terms to take
        """
        if opt_options is None:
            opt_options = {}
        self.num_opt_var = num_opt_var
        nso = 2 * self.sdim
        operator_pool: List[Union[ABCHamiltonian, SumOfSquaresOperator]] = []
        operator_pool_fqe: List[
            Union[ABCHamiltonian, SumOfSquaresOperator]] = []
        existing_parameters: List[float] = []
        self.energies = []
        self.energies = [initial_wf.expectationValue(self.k2_fop)]
        self.residuals = []
        iteration = 0
        while iteration < self.iter_max:
            # get current wavefunction
            wf = copy.deepcopy(initial_wf)
            for op, coeff in zip(operator_pool_fqe, existing_parameters):
                if np.isclose(coeff, 0):
                    continue
                if isinstance(op, ABCHamiltonian):
                    wf = wf.time_evolve(coeff, op)
                elif isinstance(op, SumOfSquaresOperator):
                    for v, cc in zip(op.basis_rotation, op.charge_charge):
                        wf = evolve_fqe_givens_unrestricted(wf, v.conj().T)
                        wf = evolve_fqe_charge_charge_unrestricted(
                            wf, coeff * cc)
                        wf = evolve_fqe_givens_unrestricted(wf, v)
                    wf = evolve_fqe_givens_unrestricted(wf,
                                                        op.one_body_rotation)
                else:
                    raise ValueError("Can't evolve operator type {}".format(
                        type(op)))

            # calculate rdms for grad
            _, tpdm = wf.sector((self.nele, self.sz)).get_openfermion_rdms()
            if v_reconstruct:
                d3 = 6 * valdemaro_reconstruction_functional(
                    tpdm / 2, self.nele)
            else:
                d3 = wf.sector((self.nele, self.sz)).get_three_pdm()

            # get ACSE Residual and 2-RDM gradient
            acse_residual = two_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm, d3)

            if generator_decomp is None:
                fop = get_fermion_op(acse_residual)
            elif generator_decomp is 'svd':
                new_residual = np.zeros_like(acse_residual)
                for p, q, r, s in product(range(nso), repeat=4):
                    new_residual[p, q, r, s] = (acse_residual[p, q, r, s] -
                                                acse_residual[s, r, q, p]) / 2

                fop = self.get_svd_tensor_decomp(new_residual, generator_rank)
            elif generator_decomp is 'takagi':
                fop = self.get_takagi_tensor_decomp(acse_residual,
                                                    generator_rank)
            else:
                raise ValueError(
                    "Generator decomp must be None, svd, or takagi")

            operator_pool.extend([fop])
            fqe_ops: List[Union[ABCHamiltonian, SumOfSquaresOperator]] = []
            if isinstance(fop, ABCHamiltonian):
                fqe_ops.append(fop)
            elif isinstance(fop, SumOfSquaresOperator):
                fqe_ops.append(fop)
            else:
                fqe_ops.append(
                    build_hamiltonian(1j * fop, self.sdim,
                                      conserve_number=True))

            operator_pool_fqe.extend(fqe_ops)
            existing_parameters.extend([0])

            if self.num_opt_var is not None:
                if len(operator_pool_fqe) < self.num_opt_var:
                    pool_to_op = operator_pool_fqe
                    params_to_op = existing_parameters
                    current_wf = copy.deepcopy(initial_wf)
                else:
                    pool_to_op = operator_pool_fqe[-self.num_opt_var:]
                    params_to_op = existing_parameters[-self.num_opt_var:]
                    current_wf = copy.deepcopy(initial_wf)
                    for fqe_op, coeff in zip(
                            operator_pool_fqe[:-self.num_opt_var],
                            existing_parameters[:-self.num_opt_var]):
                        current_wf = current_wf.time_evolve(coeff, fqe_op)
                    temp_cwf = copy.deepcopy(current_wf)
                    for fqe_op, coeff in zip(pool_to_op, params_to_op):
                        if np.isclose(coeff, 0):
                            continue
                        temp_cwf = temp_cwf.time_evolve(coeff, fqe_op)

                new_parameters, current_e = self.optimize_param(
                    pool_to_op,
                    params_to_op,
                    current_wf,
                    opt_method,
                    opt_options=opt_options)

                if len(operator_pool_fqe) < self.num_opt_var:
                    existing_parameters = new_parameters.tolist()
                else:
                    existing_parameters[-self.num_opt_var:] = \
                        new_parameters.tolist()
            else:
                new_parameters, current_e = self.optimize_param(
                    operator_pool_fqe,
                    existing_parameters,
                    initial_wf,
                    opt_method,
                    opt_options=opt_options)
                existing_parameters = new_parameters.tolist()

            if self.verbose:
                print(iteration, current_e, np.max(np.abs(acse_residual)),
                      len(existing_parameters))
            self.energies.append(current_e)
            self.residuals.append(acse_residual)
            if np.max(np.abs(acse_residual)) < self.stopping_eps or np.abs(
                    self.energies[-2] - self.energies[-1]) < self.delta_e_eps:
                break
            iteration += 1
示例#22
0
 def test_expectation_value_type_error(self):
     wfn = Wavefunction([[4, 0, 4]])
     self.assertRaises(TypeError, wfn.expectationValue, 1)
示例#23
0
    def test_hartree_fock_init(self):
        h1e, h2e, _ = build_lih_data('energy')
        elec_hamil = get_restricted_hamiltonian((h1e, h2e))
        norb = 6
        nalpha = 2
        nbeta = 2
        wfn = Wavefunction([[nalpha + nbeta, nalpha - nbeta, norb]])
        wfn.print_wfn()
        wfn.set_wfn(strategy='hartree-fock')
        wfn.print_wfn()
        self.assertEqual(wfn.expectationValue(elec_hamil), -8.857341498221992)
        hf_wf = numpy.zeros((int(binom(norb, 2)), int(binom(norb, 2))))
        hf_wf[0, 0] = 1.
        self.assertTrue(numpy.allclose(wfn.get_coeff((4, 0)), hf_wf))

        wfn = Wavefunction([[nalpha + nbeta, nalpha - nbeta, norb],
                            [nalpha + nbeta, 2, norb]])
        self.assertRaises(ValueError, wfn.set_wfn, strategy='hartree-fock')
示例#24
0
    def adapt_vqe(self,
                  initial_wf: Wavefunction,
                  opt_method: str = 'L-BFGS-B',
                  opt_options=None,
                  v_reconstruct: bool = True,
                  num_ops_add: int = 1):
        """
        Run ADAPT-VQE using

        Args:
            initial_wf: Initial wavefunction at the start of the calculation
            opt_method: scipy optimizer to use
            opt_options: options  for scipy optimizer
            v_reconstruct: use valdemoro reconstruction
            num_ops_add: add this many operators from the pool to the
                         wavefunction
        """
        if opt_options is None:
            opt_options = {}
        operator_pool = []
        operator_pool_fqe: List[ABCHamiltonian] = []
        existing_parameters: List[float] = []
        self.gradients = []
        self.energies = [initial_wf.expectationValue(self.k2_fop)]
        iteration = 0
        while iteration < self.iter_max:
            # get current wavefunction
            wf = copy.deepcopy(initial_wf)
            for fqe_op, coeff in zip(operator_pool_fqe, existing_parameters):
                wf = wf.time_evolve(coeff, fqe_op)

            # calculate rdms for grad
            _, tpdm = wf.sector((self.nele, self.sz)).get_openfermion_rdms()
            if v_reconstruct:
                d3 = 6 * valdemaro_reconstruction(tpdm / 2, self.nele)
            else:
                d3 = wf.sector((self.nele, self.sz)).get_three_pdm()

            # get ACSE Residual and 2-RDM gradient
            acse_residual = two_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm, d3)
            one_body_residual = one_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm)

            # calculate grad of each operator in the pool
            pool_grad = []
            for operator in self.operator_pool.op_pool:
                grad_val = 0
                for op_term, coeff in operator.terms.items():
                    idx = [xx[0] for xx in op_term]
                    if len(idx) == 4:
                        grad_val += acse_residual[tuple(idx)] * coeff
                    elif len(idx) == 2:
                        grad_val += one_body_residual[tuple(idx)] * coeff
                pool_grad.append(grad_val)

            max_grad_terms_idx = \
                np.argsort(np.abs(pool_grad))[::-1][:num_ops_add]

            pool_terms = [
                self.operator_pool.op_pool[i] for i in max_grad_terms_idx
            ]
            operator_pool.extend(pool_terms)
            fqe_ops: List[ABCHamiltonian] = []
            for f_op in pool_terms:
                fqe_ops.append(
                    build_hamiltonian(1j * f_op,
                                      self.sdim,
                                      conserve_number=True))
            operator_pool_fqe.extend(fqe_ops)
            existing_parameters.extend([0] * len(fqe_ops))

            new_parameters, current_e = self.optimize_param(
                operator_pool_fqe,
                existing_parameters,
                initial_wf,
                opt_method,
                opt_options=opt_options)

            existing_parameters = new_parameters.tolist()
            if self.verbose:
                print(iteration, current_e, max(np.abs(pool_grad)))
            self.energies.append(current_e)
            self.gradients.append(pool_grad)
            if max(np.abs(pool_grad)) < self.stopping_eps or np.abs(
                    self.energies[-2] - self.energies[-1]) < self.delta_e_eps:
                break
            iteration += 1
示例#25
0
def get_acse_residual_fqe(fqe_wf: Wavefunction, fqe_ham: RestrictedHamiltonian,
                          norbs: int) -> np.ndarray:
    """Get the ACSE block by using reduced density operators that are Sz spin
    adapted

    R^{ij}_{lk} = <psi | [i^ j^ k l, A] | psi>

    alpha-alpha, beta-beta, alpha-beta, and beta-alpha blocks

    we do not compression over alpha-alpha or beta-beta so these are still
    norbs**2 in linear dimension. In other words, we do computation on
    elements we know should be zero. This is for simplicity in the code.

    Args:
        fqe_wf:  fqe.Wavefunction object to calculate expectation value with
        fqe_ham: fqe.RestrictedHamiltonian operator corresponding to a chemical
                 Hamiltonian
        norbs: Number of orbitals. Number of spatial orbitals

    Returns:
        Gradient of the i^ j^ k l operator
    """
    acse_aa = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)
    acse_bb = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)
    acse_ab = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)

    fqe_appA = fqe_wf.apply(fqe_ham)

    for p, q, r, s in product(range(norbs), repeat=4):
        # alpha-alpha block real
        if p != q and r != s:
            rdo = ((2 * p, 1), (2 * q, 1), (2 * r, 0), (2 * s, 0))
            rdo = 1j * (of.FermionOperator(rdo) -
                        of.hermitian_conjugated(of.FermionOperator(rdo)))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_aa[p, q, r, s] = (val2 - val1) / 2j

            # alpha-alpha block imag
            rdo = ((2 * p, 1), (2 * q, 1), (2 * r, 0), (2 * s, 0))
            rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
                of.FermionOperator(rdo))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_aa[p, q, r, s] += (val2 - val1) / 2

            # beta-beta block real
            rdo = (
                (2 * p + 1, 1),
                (2 * q + 1, 1),
                (2 * r + 1, 0),
                (2 * s + 1, 0),
            )
            rdo = 1j * (of.FermionOperator(rdo) -
                        of.hermitian_conjugated(of.FermionOperator(rdo)))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_bb[p, q, r, s] += (val2 - val1) / 2j

            # beta-beta block imag
            rdo = (
                (2 * p + 1, 1),
                (2 * q + 1, 1),
                (2 * r + 1, 0),
                (2 * s + 1, 0),
            )
            rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
                of.FermionOperator(rdo))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_bb[p, q, r, s] += (val2 - val1) / 2

        # alpha-beta block real
        rdo = ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0))
        rdo = 1j * (of.FermionOperator(rdo) -
                    of.hermitian_conjugated(of.FermionOperator(rdo)))
        val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
        val2 = np.conjugate(val1)
        acse_ab[p, q, r, s] += (val2 - val1) / 2j

        # alpha-beta block imag
        rdo = ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0))
        rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
            of.FermionOperator(rdo))
        val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
        val2 = np.conjugate(val1)  # fqe.util.vdot(fqe_wf.apply(rdo), fqe_appA)
        acse_ab[p, q, r, s] += (val2 - val1) / 2

    # unroll residual blocks into full matrix
    acse_residual = np.zeros((2 * norbs, 2 * norbs, 2 * norbs, 2 * norbs),
                             dtype=np.complex128)
    acse_residual[::2, ::2, ::2, ::2] = acse_aa
    acse_residual[1::2, 1::2, 1::2, 1::2] = acse_bb
    acse_residual[::2, 1::2, 1::2, ::2] = acse_ab
    acse_residual[::2, 1::2, ::2, 1::2] = np.einsum("ijkl->ijlk", -acse_ab)
    acse_residual[1::2, ::2, ::2, 1::2] = np.einsum("ijkl->jilk", acse_ab)
    acse_residual[1::2, ::2,
                  1::2, ::2] = np.einsum("ijkl->ijlk",
                                         -acse_residual[1::2, ::2, ::2, 1::2])

    return acse_residual
示例#26
0
 def test_set_wfn_random_with_multiple_sectors_is_normalized(self):
     wfn = Wavefunction([[2, 0, 4], [2, -2, 4]], broken=None)
     wfn.set_wfn(strategy="random")
     self.assertAlmostEqual(wfn.norm(), 1.0)