# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests _bravyi_kitaev_fast_test.py.""" import os import unittest import numpy from openfermion.config import THIS_DIRECTORY from openfermion.hamiltonians import MolecularData from openfermion.ops import (FermionOperator, InteractionOperator, QubitOperator) from openfermion.transforms import _bksf from openfermion.transforms._conversion import (get_fermion_operator, get_sparse_operator) from openfermion.transforms._jordan_wigner import (jordan_wigner, jordan_wigner_one_body) from openfermion.utils import count_qubits, eigenspectrum, normal_ordered class bravyi_kitaev_fastTransformTest(unittest.TestCase): def setUp(self): 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') self.molecule = MolecularData( geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered(get_fermion_operator( self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian) def test_bad_input(self): with self.assertRaises(TypeError): _bksf.bravyi_kitaev_fast(FermionOperator()) def test_bravyi_kitaev_fast_edgeoperator_Bi(self): # checking the edge operators edge_matrix = numpy.triu(numpy.ones((4, 4))) edge_matrix_indices = numpy.array( numpy.nonzero(numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix)))) correct_operators_b0 = ((0, 'Z'), (1, 'Z'), (2, 'Z')) correct_operators_b1 = ((0, 'Z'), (3, 'Z'), (4, 'Z')) correct_operators_b2 = ((1, 'Z'), (3, 'Z'), (5, 'Z')) correct_operators_b3 = ((2, 'Z'), (4, 'Z'), (5, 'Z')) qterm_b0 = QubitOperator(correct_operators_b0, 1) qterm_b1 = QubitOperator(correct_operators_b1, 1) qterm_b2 = QubitOperator(correct_operators_b2, 1) qterm_b3 = QubitOperator(correct_operators_b3, 1) self.assertTrue(qterm_b0 == _bksf.edge_operator_b(edge_matrix_indices, 0)) self.assertTrue(qterm_b1 == _bksf.edge_operator_b(edge_matrix_indices, 1)) self.assertTrue(qterm_b2 == _bksf.edge_operator_b(edge_matrix_indices, 2)) self.assertTrue(qterm_b3 == _bksf.edge_operator_b(edge_matrix_indices, 3)) def test_bravyi_kitaev_fast_edgeoperator_Aij(self): # checking the edge operators edge_matrix = numpy.triu(numpy.ones((4, 4))) edge_matrix_indices = numpy.array(numpy.nonzero( numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix)))) correct_operators_a01 = ((0, 'X'),) correct_operators_a02 = ((0, 'Z'), (1, 'X')) correct_operators_a03 = ((0, 'Z'), (1, 'Z'), (2, 'X')) correct_operators_a12 = ((0, 'Z'), (1, 'Z'), (3, 'X')) correct_operators_a13 = ((0, 'Z'), (2, 'Z'), (3, 'Z'), (4, 'X')) correct_operators_a23 = ((1, 'Z'), (2, 'Z'), (3, 'Z'), (4, 'Z'), (5, 'X')) qterm_a01 = QubitOperator(correct_operators_a01, 1) qterm_a02 = QubitOperator(correct_operators_a02, 1) qterm_a03 = QubitOperator(correct_operators_a03, 1) qterm_a12 = QubitOperator(correct_operators_a12, 1) qterm_a13 = QubitOperator(correct_operators_a13, 1) qterm_a23 = QubitOperator(correct_operators_a23, 1) self.assertTrue(qterm_a01 == _bksf.edge_operator_aij( edge_matrix_indices, 0, 1)) self.assertTrue(qterm_a02 == _bksf.edge_operator_aij( edge_matrix_indices, 0, 2)) self.assertTrue(qterm_a03 == _bksf.edge_operator_aij( edge_matrix_indices, 0, 3)) self.assertTrue(qterm_a12 == _bksf.edge_operator_aij( edge_matrix_indices, 1, 2)) self.assertTrue(qterm_a13 == _bksf.edge_operator_aij( edge_matrix_indices, 1, 3)) self.assertTrue(qterm_a23 == _bksf.edge_operator_aij( edge_matrix_indices, 2, 3)) def test_bravyi_kitaev_fast_jw_number_operator(self): # bksf algorithm allows for even number of particles. So, compare the # spectrum of number operator from jordan-wigner and bksf algorithm # to make sure half of the jordan-wigner number operator spectrum # can be found in bksf number operator spectrum. bravyi_kitaev_fast_n = _bksf.number_operator( self.molecular_hamiltonian) jw_n = QubitOperator() n_qubits = count_qubits(self.molecular_hamiltonian) for i in range(n_qubits): jw_n += jordan_wigner_one_body(i, i) jw_eig_spec = eigenspectrum(jw_n) bravyi_kitaev_fast_eig_spec = eigenspectrum(bravyi_kitaev_fast_n) evensector = 0 for i in range(numpy.size(jw_eig_spec)): if bool(numpy.size(numpy.where(jw_eig_spec[i] == bravyi_kitaev_fast_eig_spec))): evensector += 1 self.assertEqual(evensector, 2**(n_qubits - 1)) def test_bravyi_kitaev_fast_jw_hamiltonian(self): # make sure half of the jordan-wigner Hamiltonian eigenspectrum can # be found in bksf Hamiltonian eigenspectrum. n_qubits = count_qubits(self.molecular_hamiltonian) bravyi_kitaev_fast_H = _bksf.bravyi_kitaev_fast( self.molecular_hamiltonian) jw_H = jordan_wigner(self.molecular_hamiltonian) bravyi_kitaev_fast_H_eig = eigenspectrum(bravyi_kitaev_fast_H) jw_H_eig = eigenspectrum(jw_H) bravyi_kitaev_fast_H_eig = bravyi_kitaev_fast_H_eig.round(5) jw_H_eig = jw_H_eig.round(5) evensector = 0 for i in range(numpy.size(jw_H_eig)): if bool(numpy.size(numpy.where(jw_H_eig[i] == bravyi_kitaev_fast_H_eig))): evensector += 1 self.assertEqual(evensector, 2 ** (n_qubits - 1)) def test_bravyi_kitaev_fast_generate_fermions(self): n_qubits = count_qubits(self.molecular_hamiltonian) # test for generating two fermions edge_matrix = _bksf.bravyi_kitaev_fast_edge_matrix( self.molecular_hamiltonian) edge_matrix_indices = numpy.array(numpy.nonzero( numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix)))) fermion_generation_operator = _bksf.generate_fermions( edge_matrix_indices, 2, 3) fermion_generation_sp_matrix = get_sparse_operator( fermion_generation_operator) fermion_generation_matrix = fermion_generation_sp_matrix.toarray() bksf_vacuum_state_operator = _bksf.vacuum_operator(edge_matrix_indices) bksf_vacuum_state_sp_matrix = get_sparse_operator( bksf_vacuum_state_operator) bksf_vacuum_state_matrix = bksf_vacuum_state_sp_matrix.toarray() vacuum_state = numpy.zeros((2**(n_qubits), 1)) vacuum_state[0] = 1. bksf_vacuum_state = numpy.dot(bksf_vacuum_state_matrix, vacuum_state) two_fermion_state = numpy.dot(fermion_generation_matrix, bksf_vacuum_state) # using total number operator to check the number of fermions generated tot_number_operator = _bksf.number_operator(self.molecular_hamiltonian) number_operator_sp_matrix = get_sparse_operator(tot_number_operator) number_operator_matrix = number_operator_sp_matrix.toarray() tot_fermions = numpy.dot(two_fermion_state.conjugate().T, numpy.dot(number_operator_matrix, two_fermion_state)) # checking the occupation number of site 2 and 3 number_operator_2 = _bksf.number_operator(self.molecular_hamiltonian, 2) number_operator_3 = _bksf.number_operator(self.molecular_hamiltonian, 3) number_operator_23 = number_operator_2 + number_operator_3 number_operator_23_sp_matrix = get_sparse_operator(number_operator_23) number_operator_23_matrix = number_operator_23_sp_matrix.toarray() tot_23_fermions = numpy.dot(two_fermion_state.conjugate().T, numpy.dot(number_operator_23_matrix, two_fermion_state)) self.assertTrue(2.0 - float(tot_fermions.real) < 1e-13) self.assertTrue(2.0 - float(tot_23_fermions.real) < 1e-13) def test_bravyi_kitaev_fast_excitation_terms(self): # Testing on-site and excitation terms in Hamiltonian constant = 0 one_body = numpy.array([[1, 2, 0, 3], [2, 1, 2, 0], [0, 2, 1, 2.5], [3, 0, 2.5, 1]]) # No Coloumb interaction two_body = numpy.zeros((4, 4, 4, 4)) molecular_hamiltonian = InteractionOperator(constant, one_body, two_body) n_qubits = count_qubits(molecular_hamiltonian) # comparing the eigenspectrum of Hamiltonian bravyi_kitaev_fast_H = _bksf.bravyi_kitaev_fast(molecular_hamiltonian) jw_H = jordan_wigner(molecular_hamiltonian) bravyi_kitaev_fast_H_eig = eigenspectrum(bravyi_kitaev_fast_H) jw_H_eig = eigenspectrum(jw_H) bravyi_kitaev_fast_H_eig = bravyi_kitaev_fast_H_eig.round(5) jw_H_eig = jw_H_eig.round(5) evensector_H = 0 for i in range(numpy.size(jw_H_eig)): if bool(numpy.size(numpy.where(jw_H_eig[i] == bravyi_kitaev_fast_H_eig))): evensector_H += 1 # comparing eigenspectrum of number operator bravyi_kitaev_fast_n = _bksf.number_operator(molecular_hamiltonian) jw_n = QubitOperator() n_qubits = count_qubits(molecular_hamiltonian) for i in range(n_qubits): jw_n += jordan_wigner_one_body(i, i) jw_eig_spec = eigenspectrum(jw_n) bravyi_kitaev_fast_eig_spec = eigenspectrum(bravyi_kitaev_fast_n) evensector_n = 0 for i in range(numpy.size(jw_eig_spec)): if bool(numpy.size(numpy.where(jw_eig_spec[i] == bravyi_kitaev_fast_eig_spec))): evensector_n += 1 self.assertEqual(evensector_H, 2**(n_qubits - 1)) self.assertEqual(evensector_n, 2**(n_qubits - 1)) def test_bravyi_kitaev_fast_number_excitation_operator(self): # using hydrogen Hamiltonian and introducing some number operator terms constant = 0 one_body = numpy.zeros((4, 4)) one_body[(0, 0)] = .4 one_body[(1, 1)] = .5 one_body[(2, 2)] = .6 one_body[(3, 3)] = .7 two_body = self.molecular_hamiltonian.two_body_tensor # initiating number operator terms for all the possible cases two_body[(1, 2, 3, 1)] = 0.1 two_body[(1, 3, 2, 1)] = 0.1 two_body[(1, 2, 1, 3)] = 0.15 two_body[(3, 1, 2, 1)] = 0.15 two_body[(0, 2, 2, 1)] = 0.09 two_body[(1, 2, 2, 0)] = 0.09 two_body[(1, 2, 3, 2)] = 0.11 two_body[(2, 3, 2, 1)] = 0.11 two_body[(2, 2, 2, 2)] = 0.1 molecular_hamiltonian = InteractionOperator(constant, one_body, two_body) # comparing the eigenspectrum of Hamiltonian n_qubits = count_qubits(molecular_hamiltonian) bravyi_kitaev_fast_H = _bksf.bravyi_kitaev_fast(molecular_hamiltonian) jw_H = jordan_wigner(molecular_hamiltonian) bravyi_kitaev_fast_H_eig = eigenspectrum(bravyi_kitaev_fast_H) jw_H_eig = eigenspectrum(jw_H) bravyi_kitaev_fast_H_eig = bravyi_kitaev_fast_H_eig.round(5) jw_H_eig = jw_H_eig.round(5) evensector_H = 0 for i in range(numpy.size(jw_H_eig)): if bool(numpy.size(numpy.where(jw_H_eig[i] == bravyi_kitaev_fast_H_eig))): evensector_H += 1 # comparing eigenspectrum of number operator bravyi_kitaev_fast_n = _bksf.number_operator(molecular_hamiltonian) jw_n = QubitOperator() n_qubits = count_qubits(molecular_hamiltonian) for i in range(n_qubits): jw_n += jordan_wigner_one_body(i, i) jw_eig_spec = eigenspectrum(jw_n) bravyi_kitaev_fast_eig_spec = eigenspectrum(bravyi_kitaev_fast_n) evensector_n = 0 for i in range(numpy.size(jw_eig_spec)): if bool(numpy.size(numpy.where(jw_eig_spec[i] == bravyi_kitaev_fast_eig_spec))): evensector_n += 1 self.assertEqual(evensector_H, 2**(n_qubits - 1)) self.assertEqual(evensector_n, 2**(n_qubits - 1)) if __name__ == '__main__': unittest.main()