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_sparse_matrix_and_numpy_array_hermitian(self): op = numpy.arange(16, dtype=complex).reshape((4, 4)) op += 1.j * op op += op.T.conj() self.assertTrue(is_hermitian(op)) op = csc_matrix(op) self.assertTrue(is_hermitian(op))
def test_quad_operator_hermitian(self): op = QuadOperator('q0 p1 q2 p3') self.assertTrue(is_hermitian(op)) op = QuadOperator('q0 p0 q1 p1') op += QuadOperator('p0 q0 p1 q1') self.assertTrue(is_hermitian(op))
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))
def test_fourier_transform(self): for length in [2, 3]: grid = Grid(dimensions=1, scale=1.5, length=length) spinless_set = [True, False] geometry = [('H', (0.1,)), ('H', (0.5,))] for spinless in spinless_set: h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless, True) h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless, False) h_plane_wave_t = fourier_transform(h_plane_wave, grid, spinless) self.assertEqual(normal_ordered(h_plane_wave_t), normal_ordered(h_dual_basis)) # Verify that all 3 are Hermitian plane_wave_operator = get_sparse_operator(h_plane_wave) dual_operator = get_sparse_operator(h_dual_basis) plane_wave_t_operator = get_sparse_operator(h_plane_wave_t) self.assertTrue(is_hermitian(plane_wave_operator)) self.assertTrue(is_hermitian(dual_operator)) self.assertTrue(is_hermitian(plane_wave_t_operator))
def get_quadratic_hamiltonian(fermion_operator, chemical_potential=0., n_qubits=None, ignore_incompatible_terms=False): r"""Convert a quadratic fermionic operator to QuadraticHamiltonian. Args: fermion_operator(FermionOperator): The operator to convert. chemical_potential(float): A chemical potential to include in the returned operator n_qubits(int): Optionally specify the total number of qubits in the system ignore_incompatible_terms(bool): This flag determines the behavior of this method when it encounters terms that are not quadratic that is, terms that are not of the form a^\dagger_p a_q. If set to True, this method will simply ignore those terms. If False, then this method will raise an error if it encounters such a term. The default setting is False. Returns: quadratic_hamiltonian: An instance of the QuadraticHamiltonian class. Raises: TypeError: Input must be a FermionOperator. TypeError: FermionOperator does not map to QuadraticHamiltonian. Warning: Even assuming that each creation or annihilation operator appears at most a constant number of times in the original operator, the runtime of this method is exponential in the number of qubits. """ if not isinstance(fermion_operator, FermionOperator): raise TypeError('Input must be a FermionOperator.') check_no_sympy(fermion_operator) if n_qubits is None: n_qubits = op_utils.count_qubits(fermion_operator) if n_qubits < op_utils.count_qubits(fermion_operator): raise ValueError('Invalid number of qubits specified.') # Normal order the terms and initialize. fermion_operator = normal_ordered(fermion_operator) constant = 0. combined_hermitian_part = numpy.zeros((n_qubits, n_qubits), complex) antisymmetric_part = numpy.zeros((n_qubits, n_qubits), complex) # Loop through terms and assign to matrix. for term in fermion_operator.terms: coefficient = fermion_operator.terms[term] # Ignore this term if the coefficient is zero if abs(coefficient) < EQ_TOLERANCE: # not testable because normal_ordered kills # fermion terms lower than EQ_TOLERANCE continue # pragma: no cover if len(term) == 0: # Constant term constant = coefficient elif len(term) == 2: ladder_type = [operator[1] for operator in term] p, q = [operator[0] for operator in term] if ladder_type == [1, 0]: combined_hermitian_part[p, q] = coefficient elif ladder_type == [1, 1]: # Need to check that the corresponding [0, 0] term is present conjugate_term = ((p, 0), (q, 0)) if conjugate_term not in fermion_operator.terms: raise QuadraticHamiltonianError( 'FermionOperator does not map ' 'to QuadraticHamiltonian (not Hermitian).') else: matching_coefficient = -fermion_operator.terms[ conjugate_term].conjugate() discrepancy = abs(coefficient - matching_coefficient) if discrepancy > EQ_TOLERANCE: raise QuadraticHamiltonianError( 'FermionOperator does not map ' 'to QuadraticHamiltonian (not Hermitian).') antisymmetric_part[p, q] += .5 * coefficient antisymmetric_part[q, p] -= .5 * coefficient else: # ladder_type == [0, 0] # Need to check that the corresponding [1, 1] term is present conjugate_term = ((p, 1), (q, 1)) if conjugate_term not in fermion_operator.terms: raise QuadraticHamiltonianError( 'FermionOperator does not map ' 'to QuadraticHamiltonian (not Hermitian).') else: matching_coefficient = -fermion_operator.terms[ conjugate_term].conjugate() discrepancy = abs(coefficient - matching_coefficient) if discrepancy > EQ_TOLERANCE: raise QuadraticHamiltonianError( 'FermionOperator does not map ' 'to QuadraticHamiltonian (not Hermitian).') antisymmetric_part[p, q] -= .5 * coefficient.conjugate() antisymmetric_part[q, p] += .5 * coefficient.conjugate() elif not ignore_incompatible_terms: # Operator contains non-quadratic terms raise QuadraticHamiltonianError('FermionOperator does not map ' 'to QuadraticHamiltonian ' '(contains non-quadratic terms).') # Compute Hermitian part hermitian_part = (combined_hermitian_part + chemical_potential * numpy.eye(n_qubits)) # Check that the operator is Hermitian if not op_utils.is_hermitian(hermitian_part): raise QuadraticHamiltonianError( 'FermionOperator does not map ' 'to QuadraticHamiltonian (not Hermitian).') # Form QuadraticHamiltonian and return. discrepancy = numpy.max(numpy.abs(antisymmetric_part)) if discrepancy < EQ_TOLERANCE: # Hamiltonian conserves particle number quadratic_hamiltonian = QuadraticHamiltonian( hermitian_part, constant=constant, chemical_potential=chemical_potential) else: # Hamiltonian does not conserve particle number quadratic_hamiltonian = QuadraticHamiltonian(hermitian_part, antisymmetric_part, constant, chemical_potential) return quadratic_hamiltonian
def test_exceptions(self): with self.assertRaises(TypeError): _ = is_hermitian('a')
def test_sparse_matrix_and_numpy_array_identity(self): op = numpy.eye(4) self.assertTrue(is_hermitian(op)) op = csc_matrix(op) self.assertTrue(is_hermitian(op))
def test_sparse_matrix_and_numpy_array_nonhermitian(self): op = numpy.arange(16).reshape((4, 4)) self.assertFalse(is_hermitian(op)) op = csc_matrix(op) self.assertFalse(is_hermitian(op))
def test_qubit_operator_hermitian(self): op = QubitOperator('X0 Y2 Z5', 1. + 2.j) op += QubitOperator('X0 Y2 Z5', 1. - 2.j) self.assertTrue(is_hermitian(op))
def test_sparse_matrix_and_numpy_array_zero(self): op = numpy.zeros((4, 4)) self.assertTrue(is_hermitian(op)) op = csc_matrix(op) self.assertTrue(is_hermitian(op))
def test_qubit_operator_identity(self): op = QubitOperator(()) self.assertTrue(is_hermitian(op))
def test_qubit_operator_nonhermitian(self): op = QubitOperator('X0 Y2 Z5', 1. + 2.j) self.assertFalse(is_hermitian(op))
def get_diagonal_coulomb_hamiltonian(fermion_operator, n_qubits=None, ignore_incompatible_terms=False): r"""Convert a FermionOperator to a DiagonalCoulombHamiltonian. Args: fermion_operator(FermionOperator): The operator to convert. n_qubits(int): Optionally specify the total number of qubits in the system ignore_incompatible_terms(bool): This flag determines the behavior of this method when it encounters terms that are not represented by the DiagonalCoulombHamiltonian class, namely, terms that are not quadratic and not quartic of the form a^\dagger_p a_p a^\dagger_q a_q. If set to True, this method will simply ignore those terms. If False, then this method will raise an error if it encounters such a term. The default setting is False. """ if not isinstance(fermion_operator, FermionOperator): raise TypeError('Input must be a FermionOperator.') check_no_sympy(fermion_operator) if n_qubits is None: n_qubits = op_utils.count_qubits(fermion_operator) if n_qubits < op_utils.count_qubits(fermion_operator): raise ValueError('Invalid number of qubits specified.') fermion_operator = normal_ordered(fermion_operator) constant = 0. one_body = numpy.zeros((n_qubits, n_qubits), complex) two_body = numpy.zeros((n_qubits, n_qubits), float) for term, coefficient in fermion_operator.terms.items(): # Ignore this term if the coefficient is zero if abs(coefficient) < EQ_TOLERANCE: # not testable because normal_ordered kills # fermion terms lower than EQ_TOLERANCE continue # pragma: no cover if len(term) == 0: constant = coefficient else: actions = [operator[1] for operator in term] if actions == [1, 0]: p, q = [operator[0] for operator in term] one_body[p, q] = coefficient elif actions == [1, 1, 0, 0]: p, q, r, s = [operator[0] for operator in term] if p == r and q == s: if abs(numpy.imag(coefficient)) > EQ_TOLERANCE: raise ValueError( 'FermionOperator does not map to ' 'DiagonalCoulombHamiltonian (not Hermitian).') coefficient = numpy.real(coefficient) two_body[p, q] = -.5 * coefficient two_body[q, p] = -.5 * coefficient elif not ignore_incompatible_terms: raise ValueError('FermionOperator does not map to ' 'DiagonalCoulombHamiltonian ' '(contains terms with indices ' '{}).'.format((p, q, r, s))) elif not ignore_incompatible_terms: raise ValueError('FermionOperator does not map to ' 'DiagonalCoulombHamiltonian (contains terms ' 'with action {}.'.format(tuple(actions))) # Check that the operator is Hermitian if not op_utils.is_hermitian(one_body): raise ValueError( 'FermionOperator does not map to DiagonalCoulombHamiltonian ' '(not Hermitian).') return DiagonalCoulombHamiltonian(one_body, two_body, constant)
def test_qubit_operator_zero(self): op = QubitOperator() self.assertTrue(is_hermitian(op))
def test_quad_operator_nonhermitian(self): op = QuadOperator('q0 p1 q1') self.assertFalse(is_hermitian(op))
def test_boson_operator_hermitian(self): op = BosonOperator('0^ 1 2^ 3') op += BosonOperator('3^ 2 1^ 0') self.assertTrue(is_hermitian(op))
def test_boson_operator_nonhermitian(self): op = BosonOperator('0^ 1 2^ 3') self.assertFalse(is_hermitian(op))
def test_boson_operator_identity(self): op = BosonOperator(()) self.assertTrue(is_hermitian(op))
def test_boson_operator_zero(self): op = BosonOperator() self.assertTrue(is_hermitian(op))