Example #1
0
    def __init__(self,
                 hamiltonian: InteractionOperator,
                 truncation_threshold: Optional[float]=None,
                 final_rank: Optional[int]=None) -> None:

        self.truncation_threshold = truncation_threshold
        self.final_rank = final_rank

        # Get the chemist matrix.
        (self.constant,
         self.one_body_coefficients,
         chemist_two_body_coefficients) = (
                 get_chemist_two_body_coefficients(hamiltonian))

        # Perform the low rank decomposition of two-body operator.
        self.eigenvalues, self.one_body_squares, _ = (
            low_rank_two_body_decomposition(
                chemist_two_body_coefficients,
                truncation_threshold=self.truncation_threshold,
                final_rank=self.final_rank))

        # Get scaled density-density terms and basis transformation matrices.
        self.scaled_density_density_matrices = []  # type: List[numpy.ndarray]
        self.basis_change_matrices = []            # type: List[numpy.ndarray]
        for j in range(len(self.eigenvalues)):
            density_density_matrix, basis_change_matrix = (
                prepare_one_body_squared_evolution(self.one_body_squares[j]))
            self.scaled_density_density_matrices.append(
                    numpy.real(self.eigenvalues[j] * density_density_matrix))
            self.basis_change_matrices.append(basis_change_matrix)

        super().__init__(hamiltonian)
Example #2
0
    def test_operator_consistency(self):

        # Initialize a random InteractionOperator and FermionOperator.
        n_qubits = 4
        random_interaction = random_interaction_operator(n_qubits,
                                                         real=False,
                                                         seed=34281)
        random_fermion = get_fermion_operator(random_interaction)

        # Convert to chemist ordered tensor.
        io_constant, io_one_body_coefficients, io_chemist_tensor = \
            get_chemist_two_body_coefficients(random_interaction)
        fo_constant, fo_one_body_coefficients, fo_chemist_tensor = \
            get_chemist_two_body_coefficients(random_fermion)

        # Ensure consistency between FermionOperator and InteractionOperator.
        self.assertAlmostEqual(io_constant, fo_constant)

        one_body_difference = numpy.sum(
            numpy.absolute(io_one_body_coefficients -
                           fo_one_body_coefficients))
        self.assertAlmostEqual(0., one_body_difference)

        two_body_difference = numpy.sum(
            numpy.absolute(io_chemist_tensor - fo_chemist_tensor))
        self.assertAlmostEqual(0., two_body_difference)

        # Convert output to FermionOperator.
        output_operator = FermionOperator()
        output_operator += FermionOperator((), fo_constant)

        # Convert one-body.
        for p, q in itertools.product(range(n_qubits), repeat=2):
            term = ((p, 1), (q, 0))
            coefficient = fo_one_body_coefficients[p, q]
            output_operator += FermionOperator(term, coefficient)

        # Convert two-body.
        for p, q, r, s in itertools.product(range(n_qubits), repeat=4):
            term = ((p, 1), (q, 0), (r, 1), (s, 0))
            coefficient = fo_chemist_tensor[p, q, r, s]
            output_operator += FermionOperator(term, coefficient)

        # Check that difference is small.
        difference = normal_ordered(random_fermion - output_operator)
        self.assertAlmostEqual(0., difference.induced_norm())
Example #3
0
    def test_one_body_square_decomposition(self):

        # Initialize a random two-body FermionOperator.
        n_qubits = 4
        random_operator = get_fermion_operator(
            random_interaction_operator(n_qubits, seed=17004))

        # Convert to chemist tensor.
        constant, one_body_coefficients, chemist_tensor = (
            get_chemist_two_body_coefficients(random_operator))

        # Perform decomposition.
        eigenvalues, one_body_squares, trunc_error = (
            low_rank_two_body_decomposition(chemist_tensor))

        # Build back two-body component.
        for l in range(n_qubits**2):

            # Get the squared one-body operator.
            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.
            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)))
Example #4
0
    def test_exception(self):

        # Initialize a bad FermionOperator.
        n_qubits = 4
        random_interaction = random_interaction_operator(n_qubits, seed=36229)
        random_fermion = get_fermion_operator(random_interaction)
        bad_term = ((1, 1), (2, 1))
        random_fermion += FermionOperator(bad_term)

        # Check for exception.
        with self.assertRaises(TypeError):
            fo_constant, fo_one_body_coefficients, fo_chemist_tensor = (
                get_chemist_two_body_coefficients(random_fermion))
Example #5
0
    def test_operator_consistency_w_spin(self):

        # Initialize a random InteractionOperator and FermionOperator.
        n_orbitals = 3
        n_qubits = 2 * n_orbitals
        random_interaction = random_interaction_operator(n_orbitals,
                                                         expand_spin=True,
                                                         real=False,
                                                         seed=8)
        random_fermion = get_fermion_operator(random_interaction)

        # Convert to chemist ordered tensor.
        one_body_correction, chemist_tensor = \
            get_chemist_two_body_coefficients(
                random_interaction.two_body_tensor, spin_basis=True)

        # Convert output to FermionOperator.
        output_operator = FermionOperator((), random_interaction.constant)

        # Convert one-body.
        one_body_coefficients = (random_interaction.one_body_tensor +
                                 one_body_correction)
        for p, q in itertools.product(range(n_qubits), repeat=2):
            term = ((p, 1), (q, 0))
            coefficient = one_body_coefficients[p, q]
            output_operator += FermionOperator(term, coefficient)

        # Convert two-body.
        for p, q, r, s in itertools.product(range(n_orbitals), repeat=4):
            coefficient = chemist_tensor[p, q, r, s]

            # Mixed spin.
            term = ((2 * p, 1), (2 * q, 0), (2 * r + 1, 1), (2 * s + 1, 0))
            output_operator += FermionOperator(term, coefficient)

            term = ((2 * p + 1, 1), (2 * q + 1, 0), (2 * r, 1), (2 * s, 0))
            output_operator += FermionOperator(term, coefficient)

            # Same spin.
            term = ((2 * p, 1), (2 * q, 0), (2 * r, 1), (2 * s, 0))
            output_operator += FermionOperator(term, coefficient)

            term = ((2 * p + 1, 1), (2 * q + 1, 0), (2 * r + 1, 1), (2 * s + 1,
                                                                     0))
            output_operator += FermionOperator(term, coefficient)

        # Check that difference is small.
        difference = normal_ordered(random_fermion - output_operator)
        self.assertAlmostEqual(0., difference.induced_norm())
Example #6
0
    def test_operator_consistency(self):

        # Initialize a random two-body FermionOperator.
        n_qubits = 4
        random_operator = get_fermion_operator(
            random_interaction_operator(n_qubits, seed=28644))

        # Convert to chemist tensor.
        constant, one_body_coefficients, chemist_tensor = (
            get_chemist_two_body_coefficients(random_operator))

        # 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]
            decomposed_operator += FermionOperator(term, coefficient)

        # Perform decomposition.
        eigenvalues, one_body_squares, trunc_error = (
            low_rank_two_body_decomposition(chemist_tensor))
        self.assertFalse(trunc_error)

        # Check for exception.
        with self.assertRaises(ValueError):
            eigenvalues, one_body_squares, trunc_error = (
                low_rank_two_body_decomposition(chemist_tensor,
                                                truncation_threshold=1.,
                                                final_rank=1))

        # Build back two-body component.
        for l in range(n_qubits**2):
            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)
            decomposed_operator += eigenvalues[l] * (one_body_operator**2)

        # Test for consistency.
        difference = normal_ordered(decomposed_operator - random_operator)
        self.assertAlmostEqual(0., difference.induced_norm())
Example #7
0
    def test_rank_reduction(self):

        # Initialize H2.
        geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))]
        basis = 'sto-3g'
        multiplicity = 1
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(geometry,
                                 basis,
                                 multiplicity,
                                 filename=filename)
        molecule.load()

        # Get molecular Hamiltonian.
        molecular_hamiltonian = molecule.get_molecular_hamiltonian()

        # Get fermion Hamiltonian.
        fermion_hamiltonian = normal_ordered(
            get_fermion_operator(molecular_hamiltonian))

        # Get chemist tensor.
        constant, one_body_coefficients, chemist_tensor = (
            get_chemist_two_body_coefficients(fermion_hamiltonian))
        n_qubits = one_body_coefficients.shape[0]

        # Rank reduce with threshold.
        errors = []
        for truncation_threshold in [1., 0.1, 0.01, 0.001]:

            # Add back one-body terms and constant.
            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]
                decomposed_operator += FermionOperator(term, coefficient)

            # Rank reduce.
            eigenvalues, one_body_squares, trunc_error = (
                low_rank_two_body_decomposition(
                    chemist_tensor, truncation_threshold=truncation_threshold))

            # Make sure error is below truncation specification.
            self.assertTrue(trunc_error < truncation_threshold)

            # Reassemble FermionOperator.
            l_max = eigenvalues.size
            for l in range(l_max):
                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)
                decomposed_operator += eigenvalues[l] * (one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator -
                                        fermion_hamiltonian)
            errors += [difference.induced_norm()]
            self.assertTrue(errors[-1] <= trunc_error
                            or abs(errors[-1] - trunc_error) < 1e-6)
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])

        # Rank reduce by setting final rank.
        errors = []
        for final_rank in [4, 6, 8, 10, 12]:

            # Add back one-body terms and constant.
            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]
                decomposed_operator += FermionOperator(term, coefficient)

            # Rank reduce.
            eigenvalues, one_body_squares, trunc_error = (
                low_rank_two_body_decomposition(chemist_tensor,
                                                final_rank=final_rank))

            # Reassemble FermionOperator.
            l_max = eigenvalues.size
            for l in range(l_max):
                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)
                decomposed_operator += eigenvalues[l] * (one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator -
                                        fermion_hamiltonian)
            errors += [difference.induced_norm()]
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])
Example #8
0
fermionic_hamiltonian = get_fermion_operator(
    molecule.get_molecular_hamiltonian())

# matrix for hamiltonian
h = np.array([0, [], [[]], [[[]]], [[[[]]]]])
h[0] = fermionic_hamiltonian.terms[()]
h[2] = np.zeros((nqubit, nqubit))
h[4] = np.zeros((nqubit, nqubit, nqubit, nqubit))

# input hamiltonian into h
for i, j, k, l in itertools.product(range(nqubit), repeat=4):
    h[4][i][j][k][l] = fermionic_hamiltonian.terms.get(
        ((i, 1), (j, 1), (k, 0), (l, 0)), 0)

# get chemist-ordered matrix (h2_correction unused)
h2_correction, V = get_chemist_two_body_coefficients(h[4], spin_basis=True)

# reshape into matrix
Vmat = V.reshape(half_nqubit**2, half_nqubit**2)

# SVD decomposition
u1, lam, u2 = np.linalg.svd(Vmat)

# Record the errors of SVD expansion in each k
V_svd = np.tensordot(u1[:, 0], u2[0, :], 0) * lam[0]
error_svd.append(np.linalg.norm(Vmat - V_svd))
for i in range(1, k_svd):
    V_svd += np.tensordot(u1[:, i], u2[i, :], 0) * lam[i]
    error_svd.append(np.linalg.norm(Vmat - V_svd))

# 第一添え字をxにするため