Exemple #1
0
    def test_integration_jellium_hamiltonian_with_negation(self):
        hamiltonian = normal_ordered(
            jellium_model(Grid(2, 3, 1.), plane_wave=False))

        part_a = FermionOperator.zero()
        part_b = FermionOperator.zero()

        add_to_a_or_b = 0  # add to a if 0; add to b if 1
        for term, coeff in hamiltonian.terms.items():
            # Partition terms in the Hamiltonian into part_a or part_b
            if add_to_a_or_b:
                part_a += FermionOperator(term, coeff)
            else:
                part_b += FermionOperator(term, coeff)
            add_to_a_or_b ^= 1

        reference = normal_ordered(commutator(part_a, part_b))
        result = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            part_a, part_b)

        self.assertTrue(result.isclose(reference))

        negative = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            part_b, part_a)
        result += negative

        self.assertTrue(result.isclose(FermionOperator.zero()))
Exemple #2
0
def test_erpa_eom_ham_lih():
    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    rha_fermion = get_fermion_operator(reduced_ham)
    permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor)
    opdm = np.diag([1] * molecule.n_electrons + [0] *
                   (molecule.n_qubits - molecule.n_electrons))
    tpdm = 2 * wedge(opdm, opdm, (1, 1), (1, 1))
    rdms = InteractionRDM(opdm, tpdm)
    dim = 3  # so we don't do the full basis.  This would make the test long
    full_basis = {}  # erpa basis.  A, B basis in RPA language
    cnt = 0
    # start from 1 to make test shorter
    for p, q in product(range(1, dim), repeat=2):
        if p < q:
            full_basis[(p, q)] = cnt
            full_basis[(q, p)] = cnt + dim * (dim - 1) // 2
            cnt += 1
    for rkey in full_basis.keys():
        p, q = rkey
        for ckey in full_basis.keys():
            r, s = ckey
            for sigma, tau in product([0, 1], repeat=2):
                test = erpa_eom_hamiltonian(permuted_hijkl, tpdm,
                                            2 * q + sigma, 2 * p + sigma,
                                            2 * r + tau, 2 * s + tau).real
                qp_op = FermionOperator(
                    ((2 * q + sigma, 1), (2 * p + sigma, 0)))
                rs_op = FermionOperator(((2 * r + tau, 1), (2 * s + tau, 0)))
                erpa_op = normal_ordered(
                    commutator(qp_op, commutator(rha_fermion, rs_op)))
                true = rdms.expectation(get_interaction_operator(erpa_op))
                assert np.isclose(true, test)
def one_body_fermion_constraints(n_orbitals, n_fermions):
    """Generates one-body positivity constraints on fermionic RDMs.

        The specific constraints implemented are known positivity constraints
        on the one-fermion reduced density matrices. Constraints are generated
        in the form of FermionOperators whose expectation value is known to be
        zero for any N-Representable state. Generators are used for efficiency.

    Args:
        n_orbitals(int): number of spin-orbitals on which operators act.
        n_fermions(int): number of fermions in the system.

    Yields:
        Constraint is a FermionOperator with zero expectation value.
    """
    # One-RDM trace condition.
    constraint_operator = FermionOperator()
    for i in range(n_orbitals):
        constraint_operator += FermionOperator(((i, 1), (i, 0)))
    if len(constraint_operator.terms):
        constraint_operator -= FermionOperator((), n_fermions)
        yield constraint_operator

    # One-RDM Hermiticity condition.
    for i in range(n_orbitals):
        for j in range(i + 1, n_orbitals):
            constraint_operator = FermionOperator(((i, 1), (j, 0)))
            constraint_operator -= FermionOperator(((j, 1), (i, 0)))
            if len(constraint_operator.terms):
                yield constraint_operator
def sx_operator(n_spatial_orbitals: int) -> FermionOperator:
    r"""Return the sx operator.

    $$
        \begin{align}
        S^{x} = \frac{1}{2}\sum_{i = 1}^{n}(S^{+} + S^{-})
        \end{align}
    $$

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits // 2).

    Returns:
        operator (FermionOperator): corresponding to the sx operator over
        n_spatial_orbitals.

    Note:
        The indexing convention used is that even indices correspond to
        spin-up (alpha) modes and odd indices correspond to spin-down (beta)
        modes.
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((up_index(ni), 1), (down_index(ni), 0)),
                                    .5)
        operator += FermionOperator(((down_index(ni), 1), (up_index(ni), 0)),
                                    .5)

    return operator
Exemple #5
0
def plane_wave_kinetic(grid: Grid,
                       spinless: bool = False,
                       e_cutoff: Optional[float] = None) -> FermionOperator:
    """Return the kinetic energy operator in the plane wave basis.

    Args:
        grid (openfermion.utils.Grid): The discretization to use.
        spinless (bool): Whether to use the spinless model or not.
        e_cutoff (float): Energy cutoff.

    Returns:
        FermionOperator: The kinetic momentum operator.
    """
    # Initialize.
    operator = FermionOperator()
    spins = [None] if spinless else [0, 1]

    # Loop once through all plane waves.
    for momenta_indices in grid.all_points_indices():
        momenta = grid.momentum_vector(momenta_indices)
        coefficient = momenta.dot(momenta) / 2.

        # Energy cutoff.
        if e_cutoff is not None and coefficient > e_cutoff:
            continue

        # Loop over spins.
        for spin in spins:
            orbital = grid.orbital_id(momenta_indices, spin)

            # Add interaction term.
            operators = ((orbital, 1), (orbital, 0))
            operator += FermionOperator(operators, coefficient)

    return operator
Exemple #6
0
def get_interaction_rdm(qubit_operator, n_qubits=None):
    """Build an InteractionRDM from measured qubit operators.

    Returns: An InteractionRDM object.
    """

    check_no_sympy(qubit_operator)

    # Avoid circular import.
    from openfermion.transforms import jordan_wigner
    if n_qubits is None:
        n_qubits = count_qubits(qubit_operator)
    one_rdm = numpy.zeros((n_qubits, ) * 2, dtype=complex)
    two_rdm = numpy.zeros((n_qubits, ) * 4, dtype=complex)

    # One-RDM.
    for i, j in itertools.product(range(n_qubits), repeat=2):
        transformed_operator = jordan_wigner(FermionOperator(((i, 1), (j, 0))))
        for term, coefficient in transformed_operator.terms.items():
            if term in qubit_operator.terms:
                one_rdm[i, j] += coefficient * qubit_operator.terms[term]

    # Two-RDM.
    for i, j, k, l in itertools.product(range(n_qubits), repeat=4):
        transformed_operator = jordan_wigner(
            FermionOperator(((i, 1), (j, 1), (k, 0), (l, 0))))
        for term, coefficient in transformed_operator.terms.items():
            if term in qubit_operator.terms:
                two_rdm[i, j, k, l] += coefficient * qubit_operator.terms[term]

    return InteractionRDM(one_rdm, two_rdm)
def dual_basis_external_potential(
        grid: Grid,
        geometry: List[Tuple[str, Tuple[Union[int, float], Union[int, float],
                                        Union[int, float]]]],
        spinless: bool,
        non_periodic: bool = False,
        period_cutoff: Optional[float] = None) -> FermionOperator:
    """Return the external potential in the dual basis of arXiv:1706.00023.

    The external potential resulting from electrons interacting with nuclei
        in the plane wave dual basis.  Note that a cos term is used which is
        strictly only equivalent under aliasing in odd grids, and amounts
        to the addition of an extra term to make the diagonals real on even
        grids.  This approximation is not expected to be significant and allows
        for use of even and odd grids on an even footing.

    Args:
        grid (Grid): The discretization to use.
        geometry: A list of tuples giving the coordinates of each atom.
            example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))].
            Distances in atomic units. Use atomic symbols to specify atoms.
        spinless (bool): Whether to use the spinless model or not.
        non_periodic (bool): If the system is non-periodic, default to False.
        period_cutoff (float): Period cutoff, default to
            grid.volume_scale() ** (1. / grid.dimensions)

    Returns:
        FermionOperator: The dual basis operator.
    """
    prefactor = -4.0 * np.pi / grid.volume_scale()
    if non_periodic and period_cutoff is None:
        period_cutoff = grid.volume_scale()**(1. / grid.dimensions)
    operator = None
    if spinless:
        spins = [None]
    else:
        spins = [0, 1]
    for pos_indices in grid.all_points_indices():
        coordinate_p = grid.position_vector(pos_indices)
        for nuclear_term in geometry:
            coordinate_j = np.array(nuclear_term[1], float)
            for momenta_indices in grid.all_points_indices():
                momenta = grid.momentum_vector(momenta_indices)
                momenta_squared = momenta.dot(momenta)
                if momenta_squared == 0:
                    continue

                cos_index = momenta.dot(coordinate_j - coordinate_p)
                coefficient = (prefactor / momenta_squared *
                               md.periodic_hash_table[nuclear_term[0]] *
                               np.cos(cos_index))

                for spin_p in spins:
                    orbital_p = grid.orbital_id(pos_indices, spin_p)
                    operators = ((orbital_p, 1), (orbital_p, 0))
                    if operator is None:
                        operator = FermionOperator(operators, coefficient)
                    else:
                        operator += FermionOperator(operators, coefficient)
    return operator
Exemple #8
0
    def test_fermion_operator_hermitian(self):
        op = FermionOperator('0^ 1 2^ 3')
        op += FermionOperator('3^ 2 1^ 0')
        self.assertTrue(is_hermitian(op))

        op = fermi_hubbard(2, 2, 1., 1.)
        self.assertTrue(is_hermitian(op))
 def test_sy_operator(self):
     op = sy_operator(2)
     expected = (FermionOperator(((0, 1), (1, 0)), -0.5j) - FermionOperator(
         ((1, 1), (0, 0)), -0.5j) + FermionOperator(
             ((2, 1), (3, 0)), -0.5j) - FermionOperator(
                 ((3, 1), (2, 0)), -0.5j))
     self.assertEqual(op, expected)
Exemple #10
0
    def test_bravyi_kitaev_transform(self):
        # Check that the QubitOperators are two-term.
        lowering = bravyi_kitaev(FermionOperator(((3, 0),)))
        raising = bravyi_kitaev(FermionOperator(((3, 1),)))
        self.assertEqual(len(raising.terms), 2)
        self.assertEqual(len(lowering.terms), 2)

        #  Test the locality invariant for N=2^d qubits
        # (c_j majorana is always log2N+1 local on qubits)
        n_qubits = 16
        invariant = numpy.log2(n_qubits) + 1
        for index in range(n_qubits):
            operator = bravyi_kitaev(FermionOperator(((index, 0),)), n_qubits)
            qubit_terms = operator.terms.items()  # Get the majorana terms.

            for item in qubit_terms:
                coeff = item[1]

                #  Identify the c majorana terms by real
                #  coefficients and check their length.
                if not isinstance(coeff, complex):
                    self.assertEqual(len(item[0]), invariant)

        #  Hardcoded coefficient test on 16 qubits
        lowering = bravyi_kitaev(FermionOperator(((9, 0),)), n_qubits)
        raising = bravyi_kitaev(FermionOperator(((9, 1),)), n_qubits)

        correct_operators_c = ((7, 'Z'), (8, 'Z'), (9, 'X'), (11, 'X'), (15,
                                                                         'X'))
        correct_operators_d = ((7, 'Z'), (9, 'Y'), (11, 'X'), (15, 'X'))

        self.assertEqual(lowering.terms[correct_operators_c], 0.5)
        self.assertEqual(lowering.terms[correct_operators_d], 0.5j)
        self.assertEqual(raising.terms[correct_operators_d], -0.5j)
        self.assertEqual(raising.terms[correct_operators_c], 0.5)
Exemple #11
0
def _fourier_transform_helper(hamiltonian, grid, spinless, phase_factor,
                              vec_func_1, vec_func_2):
    hamiltonian_t = FermionOperator.zero()
    normalize_factor = numpy.sqrt(1.0 / float(grid.num_points))

    for term in hamiltonian.terms:
        transformed_term = FermionOperator.identity()
        for ladder_op_mode, ladder_op_type in term:
            indices_1 = grid.grid_indices(ladder_op_mode, spinless)
            vec1 = vec_func_1(indices_1)
            new_basis = FermionOperator.zero()
            for indices_2 in grid.all_points_indices():
                vec2 = vec_func_2(indices_2)
                spin = None if spinless else ladder_op_mode % 2
                orbital = grid.orbital_id(indices_2, spin)
                exp_index = phase_factor * 1.0j * numpy.dot(vec1, vec2)
                if ladder_op_type == 1:
                    exp_index *= -1.0

                element = FermionOperator(((orbital, ladder_op_type), ),
                                          numpy.exp(exp_index))
                new_basis += element

            new_basis *= normalize_factor
            transformed_term *= new_basis

        # Coefficient.
        transformed_term *= hamiltonian.terms[term]

        hamiltonian_t += transformed_term

    return hamiltonian_t
def s_minus_operator(n_spatial_orbitals: int) -> FermionOperator:
    r"""Return the s+ operator.

    .. math::
        \begin{align}
        S^{-} = \sum_{i=1}^{n} a_{i, \beta}^{\dagger}a_{i, \alpha}
        \end{align}

    Args:
        n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2).

    Returns:
        operator (FermionOperator): corresponding to the s- operator over
        n_spatial_orbitals.

    Note:
        The indexing convention used is that even indices correspond to
        spin-up (alpha) modes and odd indices correspond to spin-down (beta)
        modes.
    """
    if not isinstance(n_spatial_orbitals, int):
        raise TypeError("n_orbitals must be specified as an integer")

    operator = FermionOperator()
    for ni in range(n_spatial_orbitals):
        operator += FermionOperator(((down_index(ni), 1), (up_index(ni), 0)))

    return operator
Exemple #13
0
    def test_jellium_hamiltonian_correctly_broken_up(self):
        grid = Grid(2, 3, 1.)

        hamiltonian = jellium_model(grid, spinless=True, plane_wave=False)

        potential_terms, kinetic_terms = (
            diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian)
        )

        potential = sum(potential_terms, FermionOperator.zero())
        kinetic = sum(kinetic_terms, FermionOperator.zero())

        true_potential = dual_basis_jellium_model(grid,
                                                  spinless=True,
                                                  kinetic=False)
        true_kinetic = dual_basis_jellium_model(grid,
                                                spinless=True,
                                                potential=False)
        for i in range(count_qubits(true_kinetic)):
            coeff = true_kinetic.terms.get(((i, 1), (i, 0)))
            if coeff:
                true_kinetic -= FermionOperator(((i, 1), (i, 0)), coeff)
                true_potential += FermionOperator(((i, 1), (i, 0)), coeff)

        self.assertEqual(potential, true_potential)
        self.assertEqual(kinetic, true_kinetic)
Exemple #14
0
def load_operator(file_name=None, data_directory=None, plain_text=False):
    """Load FermionOperator or QubitOperator from file.

    Args:
        file_name: The name of the saved file.
        data_directory: Optional data directory to change from default data
                        directory specified in config file.
        plain_text: Whether the input file is plain text

    Returns:
        operator: The stored FermionOperator, BosonOperator,
            QuadOperator, or QubitOperator

    Raises:
        TypeError: Operator of invalid type.
    """
    file_path = get_file_path(file_name, data_directory)

    if plain_text:
        with open(file_path, 'r') as f:
            data = f.read()
            operator_type, operator_terms = data.split(":\n")

        if operator_type == 'FermionOperator':
            operator = FermionOperator(operator_terms)
        elif operator_type == 'BosonOperator':
            operator = BosonOperator(operator_terms)
        elif operator_type == 'QubitOperator':
            operator = QubitOperator(operator_terms)
        elif operator_type == 'QuadOperator':
            operator = QuadOperator(operator_terms)
        else:
            raise TypeError('Operator of invalid type.')
    else:
        with open(file_path, 'rb') as f:
            data = marshal.load(f)
            operator_type = data[0]
            operator_terms = data[1]

        if operator_type == 'FermionOperator':
            operator = FermionOperator()
            for term in operator_terms:
                operator += FermionOperator(term, operator_terms[term])
        elif operator_type == 'BosonOperator':
            operator = BosonOperator()
            for term in operator_terms:
                operator += BosonOperator(term, operator_terms[term])
        elif operator_type == 'QubitOperator':
            operator = QubitOperator()
            for term in operator_terms:
                operator += QubitOperator(term, operator_terms[term])
        elif operator_type == 'QuadOperator':
            operator = QuadOperator()
            for term in operator_terms:
                operator += QuadOperator(term, operator_terms[term])
        else:
            raise TypeError('Operator of invalid type.')

    return operator
    def test_threshold(self):
        op = get_diagonal_coulomb_hamiltonian(FermionOperator('1^ 1', 0))
        self.assertEqual(op.constant, 0)

        fop = FermionOperator('1^ 1')
        fop *= 0.5 * EQ_TOLERANCE
        op = get_diagonal_coulomb_hamiltonian(fop)
        self.assertEqual(op.constant, 0)
Exemple #16
0
 def test_jw_convention(self):
     """Test that the Jordan-Wigner convention places the Z-string on
     lower indices."""
     qubit_op = QubitOperator('Z0 X1')
     transformed_op = reverse_jordan_wigner(qubit_op)
     expected_op = FermionOperator('1^')
     expected_op += FermionOperator('1')
     self.assertTrue(transformed_op == expected_op)
    def test_fermion_triple(self):
        op_132 = FermionOperator(((1, 1), (3, 0), (2, 0)))
        op_123 = FermionOperator(((1, 1), (2, 0), (3, 0)))
        op_321 = FermionOperator(((3, 0), (2, 0), (1, 1)))

        self.assertTrue(op_132 == normal_ordered(-op_123))
        self.assertTrue(op_132 == normal_ordered(op_132))
        self.assertTrue(op_132 == normal_ordered(op_321))
Exemple #18
0
    def test_hermitian_conjugated_complex_const(self):
        op = FermionOperator('2^ 2', 3j)
        op_hc = FermionOperator('2^ 2', -3j)
        self.assertEqual(op_hc, hermitian_conjugated(op))

        op = BosonOperator('2^ 2', 3j)
        op_hc = BosonOperator('2^ 2', -3j)
        self.assertEqual(op_hc, hermitian_conjugated(op))
Exemple #19
0
    def test_hermitian_conjugated_multiterm(self):
        op = FermionOperator('1^ 2') + FermionOperator('2 3 4')
        op_hc = FermionOperator('2^ 1') + FermionOperator('4^ 3^ 2^')
        self.assertEqual(op_hc, hermitian_conjugated(op))

        op = BosonOperator('1^ 2') + BosonOperator('2 3 4')
        op_hc = BosonOperator('2^ 1') + BosonOperator('4^ 3^ 2^')
        self.assertEqual(op_hc, hermitian_conjugated(op))
Exemple #20
0
    def test_hermitian_conjugate_empty(self):
        op = FermionOperator()
        op = hermitian_conjugated(op)
        self.assertEqual(op, FermionOperator())

        op = BosonOperator()
        op = hermitian_conjugated(op)
        self.assertEqual(op, BosonOperator())
Exemple #21
0
    def test_hermitian_conjugated_simple(self):
        op = FermionOperator('0')
        op_hc = FermionOperator('0^')
        self.assertEqual(op_hc, hermitian_conjugated(op))

        op = BosonOperator('0')
        op_hc = BosonOperator('0^')
        self.assertEqual(op_hc, hermitian_conjugated(op))
Exemple #22
0
 def test_commutator(self):
     operator_a = FermionOperator('')
     self.assertEqual(FermionOperator.zero(),
                      commutator(operator_a, self.fermion_operator))
     operator_b = QubitOperator('X1 Y2')
     self.assertEqual(commutator(self.qubit_operator, operator_b),
                      (self.qubit_operator * operator_b -
                       operator_b * self.qubit_operator))
Exemple #23
0
    def test_commutator_hopping_operators(self):
        com = commutator(3 * FermionOperator('1^ 2'), FermionOperator('2^ 3'))
        com = normal_ordered(com)
        self.assertEqual(com, FermionOperator('1^ 3', 3))

        com = commutator(3 * BosonOperator('1^ 2'), BosonOperator('2^ 3'))
        com = normal_ordered(com)
        self.assertTrue(com == BosonOperator('1^ 3', 3))
Exemple #24
0
    def test_commutes_number_operators(self):
        com = commutator(FermionOperator('4^ 3^ 4 3'), FermionOperator('2^ 2'))
        com = normal_ordered(com)
        self.assertEqual(com, FermionOperator.zero())

        com = commutator(BosonOperator('4^ 3^ 4 3'), BosonOperator('2^ 2'))
        com = normal_ordered(com)
        self.assertTrue(com == BosonOperator.zero())
Exemple #25
0
    def test_identity_recognized_as_potential_term(self):
        potential_terms, kinetic_terms = (
            diagonal_coulomb_potential_and_kinetic_terms_as_arrays(
                FermionOperator.identity()))

        self.assertListEqual(list(potential_terms),
                             [FermionOperator.identity()])
        self.assertListEqual(list(kinetic_terms), [])
Exemple #26
0
 def test_bk_n_qubits_too_small(self):
     with self.assertRaises(ValueError):
         bravyi_kitaev(FermionOperator('2^ 3^ 5 0'), n_qubits=4)
     with self.assertRaises(ValueError):
         bravyi_kitaev(MajoranaOperator((2, 3, 9, 0)), n_qubits=4)
     with self.assertRaises(ValueError):
         bravyi_kitaev(get_interaction_operator(
             FermionOperator('2^ 3^ 5 0')),
                       n_qubits=4)
Exemple #27
0
 def test_get_interaction_operator_one_body_twoterm(self):
     interaction_operator = get_interaction_operator(
         FermionOperator('2^ 3', -2j) + FermionOperator('3^ 2', 3j),
         self.n_qubits)
     one_body = numpy.zeros((self.n_qubits, self.n_qubits), complex)
     one_body[2, 3] = -2j
     one_body[3, 2] = 3j
     self.assertEqual(interaction_operator,
                      InteractionOperator(0.0, one_body, self.two_body))
Exemple #28
0
 def test_ccr_onsite(self):
     c1 = FermionOperator(((1, 1), ))
     a1 = hermitian_conjugated(c1)
     self.assertTrue(
         normal_ordered(c1 * a1) == FermionOperator(()) -
         normal_ordered(a1 * c1))
     self.assertTrue(
         jordan_wigner(c1 * a1) == QubitOperator(()) -
         jordan_wigner(a1 * c1))
Exemple #29
0
    def test_one_body_square_decomposition(self):

        # Initialize H2 InteractionOperator.
        n_qubits = 4
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(filename=filename)
        molecule_interaction = molecule.get_molecular_hamiltonian()
        get_fermion_operator(molecule_interaction)

        two_body_coefficients = molecule_interaction.two_body_tensor

        # Decompose.
        eigenvalues, one_body_squares, _, _ = (low_rank_two_body_decomposition(
            two_body_coefficients, truncation_threshold=0))
        rank = eigenvalues.size
        for l in range(rank):
            one_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_squares[l, p, q]
                one_body_operator += FermionOperator(term, coefficient)
            one_body_squared = one_body_operator**2

            # Get the squared one-body operator via one-body decomposition.
            if abs(eigenvalues[l]) < 1e-6:
                with self.assertRaises(ValueError):
                    prepare_one_body_squared_evolution(one_body_squares[l])
                continue
            else:
                density_density_matrix, basis_transformation_matrix = (
                    prepare_one_body_squared_evolution(one_body_squares[l]))
            two_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (p, 0), (q, 1), (q, 0))
                coefficient = density_density_matrix[p, q]
                two_body_operator += FermionOperator(term, coefficient)

            # Confirm that the rotations diagonalize the one-body squares.
            hopefully_diagonal = basis_transformation_matrix.dot(
                numpy.dot(
                    one_body_squares[l],
                    numpy.transpose(
                        numpy.conjugate(basis_transformation_matrix))))
            diagonal = numpy.diag(hopefully_diagonal)
            difference = hopefully_diagonal - numpy.diag(diagonal)
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))
            density_density_alternative = numpy.outer(diagonal, diagonal)
            difference = density_density_alternative - density_density_matrix
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))

            # Test spectra.
            one_body_squared_spectrum = eigenspectrum(one_body_squared)
            two_body_spectrum = eigenspectrum(two_body_operator)
            difference = two_body_spectrum - one_body_squared_spectrum
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))
Exemple #30
0
    def test_molecular_operator_consistency(self):

        # Initialize H2 InteractionOperator.
        n_qubits = 4
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(filename=filename)
        molecule_interaction = molecule.get_molecular_hamiltonian()
        molecule_operator = get_fermion_operator(molecule_interaction)

        constant = molecule_interaction.constant
        one_body_coefficients = molecule_interaction.one_body_tensor
        two_body_coefficients = molecule_interaction.two_body_tensor

        # Perform decomposition.
        eigenvalues, one_body_squares, one_body_corrections, trunc_error = (
            low_rank_two_body_decomposition(two_body_coefficients))
        self.assertAlmostEqual(trunc_error, 0.)

        # Build back operator constant and one-body components.
        decomposed_operator = FermionOperator((), constant)
        for p, q in itertools.product(range(n_qubits), repeat=2):
            term = ((p, 1), (q, 0))
            coefficient = (one_body_coefficients[p, q] +
                           one_body_corrections[p, q])
            decomposed_operator += FermionOperator(term, coefficient)

        # Build back two-body component.
        for l in range(one_body_squares.shape[0]):
            one_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_squares[l, p, q]
                if abs(eigenvalues[l]) > 1e-6:
                    self.assertTrue(is_hermitian(one_body_squares[l]))
                one_body_operator += FermionOperator(term, coefficient)
            decomposed_operator += eigenvalues[l] * (one_body_operator**2)

        # Test for consistency.
        difference = normal_ordered(decomposed_operator - molecule_operator)
        self.assertAlmostEqual(0., difference.induced_norm())

        # Decompose with slightly negative operator that must use eigen
        molecule = MolecularData(filename=filename)
        molecule.two_body_integrals[0, 0, 0, 0] -= 1

        eigenvalues, one_body_squares, one_body_corrections, trunc_error = (
            low_rank_two_body_decomposition(two_body_coefficients))
        self.assertAlmostEqual(trunc_error, 0.)

        # Check for property errors
        with self.assertRaises(TypeError):
            eigenvalues, one_body_squares, _, trunc_error = (
                low_rank_two_body_decomposition(two_body_coefficients + 0.01j,
                                                truncation_threshold=1.,
                                                final_rank=1))