def test_random(self): # Create random DiagonalCoulombHamiltonian. n_qubits = 8 random.seed(n_qubits) one_body = numpy.zeros((n_qubits, n_qubits), float) two_body = numpy.zeros((n_qubits, n_qubits), float) for p in range(8): for q in range(p, 8): one_body[p, q] = random.random() two_body[p, q] = random.random() one_body += one_body.T two_body += two_body.T diagonal_operator = DiagonalCoulombHamiltonian( one_body, two_body, random.random()) # Compute the lambda norm using expensive (reliable) method. qubit_operator = jordan_wigner(diagonal_operator) del qubit_operator.terms[()] reliable_norm = qubit_operator.induced_norm() # Compute the lambda norm using the method under test. test_norm = lambda_norm(diagonal_operator) # Third norm. third_norm = 0. for term, coefficient in qubit_operator.terms.items(): third_norm += abs(coefficient) # Test. self.assertAlmostEqual(third_norm, reliable_norm) self.assertAlmostEqual(test_norm, reliable_norm)
def random_diagonal_coulomb_hamiltonian(n_qubits, real=False): """Generate a random instance of DiagonalCoulombHamiltonian. Args: n_qubits: The number of qubits real: Whether to use only real numbers in the one-body term """ one_body = random_hermitian_matrix(n_qubits, real=real) two_body = random_hermitian_matrix(n_qubits, real=True) constant = numpy.random.randn() return DiagonalCoulombHamiltonian(one_body, two_body, constant)
def get_diagonal_coulomb_hamiltonian(fermion_operator, n_qubits=None): """Convert a FermionOperator to a DiagonalCoulombHamiltonian.""" if not isinstance(fermion_operator, FermionOperator): raise TypeError('Input must be a FermionOperator.') if n_qubits is None: n_qubits = count_qubits(fermion_operator) if n_qubits < 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: continue 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 else: raise ValueError('FermionOperator does not map to ' 'DiagonalCoulombHamiltonian ' '(contains terms with indices ' '{}).'.format((p, q, r, s))) else: raise ValueError('FermionOperator does not map to ' 'DiagonalCoulombHamiltonian (contains terms ' 'with action {}.'.format(tuple(actions))) # Check that the operator is Hermitian if not is_hermitian(one_body): raise ValueError( 'FermionOperator does not map to DiagonalCoulombHamiltonian ' '(not Hermitian).') return DiagonalCoulombHamiltonian(one_body, two_body, constant)
def test_diagonal_coulomb_hamiltonian(self): n_qubits = 5 one_body = random_hermitian_matrix(n_qubits, real=False) two_body = random_hermitian_matrix(n_qubits, real=True) constant = numpy.random.randn() op = DiagonalCoulombHamiltonian(one_body, two_body, constant) op1 = get_sparse_operator(op) op2 = get_sparse_operator(jordan_wigner(get_fermion_operator(op))) diff = op1 - op2 discrepancy = 0. if diff.nnz: discrepancy = max(abs(diff.data)) self.assertAlmostEqual(discrepancy, 0.)
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.') if n_qubits is None: n_qubits = count_qubits(fermion_operator) if n_qubits < 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: continue 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 is_hermitian(one_body): raise ValueError( 'FermionOperator does not map to DiagonalCoulombHamiltonian ' '(not Hermitian).') return DiagonalCoulombHamiltonian(one_body, two_body, constant)