def is_hermitian(operator): """Test if operator is Hermitian.""" # Handle FermionOperator, BosonOperator, and InteractionOperator if isinstance(operator, (FermionOperator, BosonOperator, InteractionOperator)): return (normal_ordered(operator) == normal_ordered( hermitian_conjugated(operator))) # Handle QubitOperator and QuadOperator if isinstance(operator, (QubitOperator, QuadOperator)): return operator == hermitian_conjugated(operator) # Handle sparse matrix elif isinstance(operator, spmatrix): difference = operator - hermitian_conjugated(operator) discrepancy = 0. if difference.nnz: discrepancy = max(abs(difference.data)) return discrepancy < EQ_TOLERANCE # Handle numpy array elif isinstance(operator, numpy.ndarray): difference = operator - hermitian_conjugated(operator) discrepancy = numpy.amax(abs(difference)) return discrepancy < EQ_TOLERANCE # Unsupported type else: raise TypeError('Checking whether a {} is hermitian is not ' 'supported.'.format(type(operator).__name__))
def test_quad_triple(self): op_132 = QuadOperator(((1, 'p'), (3, 'q'), (2, 'q'))) op_123 = QuadOperator(((1, 'p'), (2, 'q'), (3, 'q'))) op_321 = QuadOperator(((3, 'q'), (2, 'q'), (1, 'p'))) self.assertTrue(op_132 == normal_ordered(op_123)) self.assertTrue(op_132 == normal_ordered(op_132)) self.assertTrue(op_132 == normal_ordered(op_321))
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))
def test_random_quadratic(self): n_qubits = 5 quad_ham = random_quadratic_hamiltonian(n_qubits, True) ferm_op = get_fermion_operator(quad_ham) self.assertTrue( normal_ordered(ferm_op) == normal_ordered( get_fermion_operator(get_diagonal_coulomb_hamiltonian( ferm_op))))
def test_convert_forward_back(self): n_qubits = 6 random_operator = get_fermion_operator( random_interaction_operator(n_qubits)) chemist_operator = chemist_ordered(random_operator) normalized_chemist = normal_ordered(chemist_operator) difference = normalized_chemist - normal_ordered(random_operator) self.assertAlmostEqual(0., difference.induced_norm())
def test_get_fermion_operator_majorana_operator(): a = MajoranaOperator((0, 3), 2.0) + MajoranaOperator((1, 2, 3)) op = get_fermion_operator(a) expected_op = (-2j * (FermionOperator(((0, 0), (1, 0))) - FermionOperator( ((0, 0), (1, 1))) + FermionOperator( ((0, 1), (1, 0))) - FermionOperator( ((0, 1), (1, 1)))) - 2 * FermionOperator( ((0, 0), (1, 1), (1, 0))) + 2 * FermionOperator( ((0, 1), (1, 1), (1, 0))) + FermionOperator( (0, 0)) - FermionOperator((0, 1))) assert normal_ordered(op) == normal_ordered(expected_op)
def test_q_squared(self): b = self.hbar * (BosonOperator('0^ 0^') + BosonOperator('0 0') + BosonOperator('') + 2 * BosonOperator('0^ 0')) / 2 q = normal_ordered(get_quad_operator(b, hbar=self.hbar), hbar=self.hbar) expected = QuadOperator('q0 q0') self.assertTrue(q == expected)
def test_interaction_operator(self): for n_orbitals, real, _ in itertools.product((1, 2, 5), (True, False), range(5)): operator = random_interaction_operator(n_orbitals, real=real) normal_ordered_operator = normal_ordered(operator) expected_qubit_operator = jordan_wigner(operator) actual_qubit_operator = jordan_wigner(normal_ordered_operator) assert expected_qubit_operator == actual_qubit_operator two_body_tensor = normal_ordered_operator.two_body_tensor n_orbitals = len(two_body_tensor) ones = numpy.ones((n_orbitals, ) * 2) triu = numpy.triu(ones, 1) shape = (n_orbitals**2, 1) mask = (triu.reshape(shape) * ones.reshape(shape[::-1]) + ones.reshape(shape) * triu.reshape(shape[::-1])).reshape( (n_orbitals, ) * 4) assert numpy.allclose(mask * two_body_tensor, numpy.zeros((n_orbitals, ) * 4)) for term in normal_ordered_operator: order = len(term) // 2 left_term, right_term = term[:order], term[order:] assert all(i[1] == 1 for i in left_term) assert all(i[1] == 0 for i in right_term) assert left_term == tuple(sorted(left_term, reverse=True)) assert right_term == tuple(sorted(right_term, reverse=True))
def test_p_squared(self): b = self.hbar * (-BosonOperator('1^ 1^') - BosonOperator('1 1') + BosonOperator('') + 2 * BosonOperator('1^ 1')) / 2 q = normal_ordered(get_quad_operator(b, hbar=self.hbar), hbar=self.hbar) expected = QuadOperator('p1 p1') self.assertTrue(q == expected)
def test_hubbard(self): x_dim = 4 y_dim = 5 tunneling = 2. coulomb = 3. chemical_potential = 7. magnetic_field = 11. periodic = False hubbard_model = fermi_hubbard(x_dim, y_dim, tunneling, coulomb, chemical_potential, magnetic_field, periodic) self.assertTrue( normal_ordered(hubbard_model) == normal_ordered( get_fermion_operator( get_diagonal_coulomb_hamiltonian(hubbard_model))))
def test_get_molecular_operator(self): coefficient = 3. operators = ((2, 1), (3, 0), (0, 0), (3, 1)) op = FermionOperator(operators, coefficient) molecular_operator = get_interaction_operator(op) fermion_operator = get_fermion_operator(molecular_operator) fermion_operator = normal_ordered(fermion_operator) self.assertTrue(normal_ordered(op) == fermion_operator) op = FermionOperator('1^ 1') op *= 0.5 * EQ_TOLERANCE molecular_operator = get_interaction_operator(op) self.assertEqual(molecular_operator.constant, 0) self.assertTrue( numpy.allclose(molecular_operator.one_body_tensor, numpy.zeros((2, 2))))
def double_commutator(op1, op2, op3, indices2=None, indices3=None, is_hopping_operator2=None, is_hopping_operator3=None): """Return the double commutator [op1, [op2, op3]]. Args: op1, op2, op3 (FermionOperators or BosonOperators): operators for the commutator. All three operators must be of the same type. indices2, indices3 (set): The indices op2 and op3 act on. is_hopping_operator2 (bool): Whether op2 is a hopping operator. is_hopping_operator3 (bool): Whether op3 is a hopping operator. Returns: The double commutator of the given operators. """ if is_hopping_operator2 and is_hopping_operator3: indices2 = set(indices2) indices3 = set(indices3) # Determine which indices both op2 and op3 act on. try: intersection, = indices2.intersection(indices3) except ValueError: return FermionOperator.zero() # Remove the intersection from the set of indices, since it will get # cancelled out in the final result. indices2.remove(intersection) indices3.remove(intersection) # Find the indices of the final output hopping operator. index2, = indices2 index3, = indices3 coeff2 = op2.terms[list(op2.terms)[0]] coeff3 = op3.terms[list(op3.terms)[0]] commutator23 = (FermionOperator( ((index2, 1), (index3, 0)), coeff2 * coeff3) + FermionOperator( ((index3, 1), (index2, 0)), -coeff2 * coeff3)) else: commutator23 = normal_ordered(commutator(op2, op3)) return normal_ordered(commutator(op1, commutator23))
def test_get_quadratic_hamiltonian_hermitian(self): """Test properly formed quadratic Hamiltonians.""" # Non-particle-number-conserving without chemical potential quadratic_op = get_quadratic_hamiltonian(self.hermitian_op) fermion_operator = get_fermion_operator(quadratic_op) fermion_operator = normal_ordered(fermion_operator) self.assertTrue(normal_ordered(self.hermitian_op) == fermion_operator) # Non-particle-number-conserving chemical potential quadratic_op = get_quadratic_hamiltonian(self.hermitian_op, chemical_potential=3.) fermion_operator = get_fermion_operator(quadratic_op) fermion_operator = normal_ordered(fermion_operator) self.assertTrue(normal_ordered(self.hermitian_op) == fermion_operator) # Particle-number-conserving quadratic_op = get_quadratic_hamiltonian(self.hermitian_op_pc) fermion_operator = get_fermion_operator(quadratic_op) fermion_operator = normal_ordered(fermion_operator) self.assertTrue( normal_ordered(self.hermitian_op_pc) == fermion_operator) fop = FermionOperator('1^ 1') fop *= 0.5E-8 quad_op = get_quadratic_hamiltonian(fop) self.assertEqual(quad_op.constant, 0)
def test_boson_two_term(self): op_b = BosonOperator(((2, 0), (4, 0), (2, 1)), 88.) normal_ordered_b = normal_ordered(op_b) expected = (BosonOperator(((4, 0), ), 88.) + BosonOperator( ((2, 1), (4, 0), (2, 0)), 88.)) self.assertTrue(normal_ordered_b == expected)
def test_quad_two_term(self): op_b = QuadOperator('p0 q0 p3', 88.) normal_ordered_b = normal_ordered(op_b, hbar=2) expected = QuadOperator('p3', -88. * 2j) + QuadOperator( 'q0 p0 p3', 88.0) self.assertTrue(normal_ordered_b == expected)
def test_quad_offsite(self): op = QuadOperator(((3, 'p'), (2, 'q'))) self.assertTrue(op == normal_ordered(op))
def test_boson_multi(self): op = BosonOperator(((2, 0), (1, 1), (2, 1))) expected = (BosonOperator(((2, 1), (1, 1), (2, 0))) + BosonOperator( ((1, 1), ))) self.assertTrue(expected == normal_ordered(op))
def test_boson_offsite(self): op = BosonOperator(((3, 1), (2, 0))) self.assertTrue(op == normal_ordered(op))
def test_boson_offsite_reversed(self): op = BosonOperator(((3, 0), (2, 1))) expected = BosonOperator(((2, 1), (3, 0))) self.assertTrue(expected == normal_ordered(op))
def test_fermion_double_create_separated(self): op = FermionOperator(((3, 1), (2, 0), (3, 1))) expected = FermionOperator((), 0.0) self.assertTrue(expected == normal_ordered(op))
def test_boson_number_reversed(self): n_term_rev2 = BosonOperator(((2, 0), (2, 1))) number_op2 = number_operator(3, 2, parity=1) expected = BosonOperator(()) + number_op2 self.assertTrue(normal_ordered(n_term_rev2) == expected)
def commutator_ordered_diagonal_coulomb_with_two_body_operator( operator_a, operator_b, prior_terms=None): """Compute the commutator of two-body operators provided that both are normal-ordered and that the first only has diagonal Coulomb interactions. Args: operator_a: The first FermionOperator argument of the commutator. All terms must be normal-ordered, and furthermore either hopping operators (i^ j) or diagonal Coulomb operators (i^ i or i^ j^ i j). operator_b: The second FermionOperator argument of the commutator. operator_b can be any arbitrary two-body operator. prior_terms (optional): The initial FermionOperator to add to. Returns: The commutator, or the commutator added to prior_terms if provided. Notes: The function could be readily extended to the case of arbitrary two-body operator_a given that operator_b has the desired form; however, the extra check slows it down without desirable added utility. """ if prior_terms is None: prior_terms = FermionOperator.zero() for term_a in operator_a.terms: coeff_a = operator_a.terms[term_a] for term_b in operator_b.terms: coeff_b = operator_b.terms[term_b] coefficient = coeff_a * coeff_b # If term_a == term_b the terms commute, nothing to add. if term_a == term_b or not term_a or not term_b: continue # Case 1: both operators are two-body, operator_a is i^ j^ i j. if (len(term_a) == len(term_b) == 4 and term_a[0][0] == term_a[2][0] and term_a[1][0] == term_a[3][0]): _commutator_two_body_diagonal_with_two_body( term_a, term_b, coefficient, prior_terms) # Case 2: commutator of a 1-body and a 2-body operator elif (len(term_b) == 4 and len(term_a) == 2) or (len(term_a) == 4 and len(term_b) == 2): _commutator_one_body_with_two_body(term_a, term_b, coefficient, prior_terms) # Case 3: both terms are one-body operators (both length 2) elif len(term_a) == 2 and len(term_b) == 2: _commutator_one_body_with_one_body(term_a, term_b, coefficient, prior_terms) # Final case (case 4): violation of the input promise. Still # compute the commutator, but warn the user. else: warnings.warn('Defaulted to standard commutator evaluation ' 'due to an out-of-spec operator.') additional = FermionOperator.zero() additional.terms[term_a + term_b] = coefficient additional.terms[term_b + term_a] = -coefficient additional = term_reordering.normal_ordered(additional) prior_terms += additional return prior_terms
def test_boson_single_term(self): op = BosonOperator('4 3 2 1') + BosonOperator('3 2') self.assertTrue(op == normal_ordered(op))
def test_quad_offsite_reversed(self): op = QuadOperator(((3, 'q'), (2, 'p'))) expected = QuadOperator(((2, 'p'), (3, 'q'))) self.assertTrue(expected == normal_ordered(op))
def test_exceptions(self): with self.assertRaises(TypeError): _ = normal_ordered(1)
def test_fermion_number_reversed(self): n_term_rev2 = FermionOperator(((2, 0), (2, 1))) number_op2 = number_operator(3, 2) expected = FermionOperator(()) - number_op2 self.assertTrue(normal_ordered(n_term_rev2) == expected)
def test_boson_number(self): number_op2 = BosonOperator(((2, 1), (2, 0))) self.assertTrue(number_op2 == normal_ordered(number_op2))
def test_fermion_multi(self): op = FermionOperator(((2, 0), (1, 1), (2, 1))) expected = (-FermionOperator( ((2, 1), (1, 1), (2, 0))) - FermionOperator(((1, 1), ))) self.assertTrue(expected == normal_ordered(op))