def test_tapering_qubits_manual_input(self): """ Test taper_off_qubits function using LiH Hamiltonian. Checks different qubits inputs to remove manually. Test the lowest eigenvalue against the full Hamiltonian, and the full spectrum between them. """ hamiltonian, spectrum = lih_hamiltonian() qubit_hamiltonian = jordan_wigner(hamiltonian) stab1 = QubitOperator('Z0 Z2', -1.0) stab2 = QubitOperator('Z1 Z3', -1.0) tapered_ham_0_3 = taper_off_qubits(qubit_hamiltonian, [stab1, stab2], manual_input=True, fixed_positions=[0, 3]) tapered_ham_2_1 = taper_off_qubits(qubit_hamiltonian, [stab1, stab2], manual_input=True, fixed_positions=[2, 1]) tapered_spectrum_0_3 = eigenspectrum(tapered_ham_0_3) tapered_spectrum_2_1 = eigenspectrum(tapered_ham_2_1) self.assertAlmostEqual(spectrum[0], tapered_spectrum_0_3[0]) self.assertAlmostEqual(spectrum[0], tapered_spectrum_2_1[0]) self.assertTrue( numpy.allclose(tapered_spectrum_0_3, tapered_spectrum_2_1))
def vacuum_operator(edge_matrix_indices): """Use the stabilizers to find the vacuum state in bravyi_kitaev_fast. Args: edge_matrix_indices(numpy array): specifying the edges Return: An instance of QubitOperator """ # Initialize qubit operator. g = networkx.Graph() g.add_edges_from(tuple(edge_matrix_indices.transpose())) stabs = numpy.array(networkx.cycle_basis(g)) vac_operator = QubitOperator(()) for stab in stabs: A = QubitOperator(()) stab = numpy.array(stab) for i in range(numpy.size(stab)): if i == (numpy.size(stab) - 1): A = (1j) * A * edge_operator_aij(edge_matrix_indices, stab[i], stab[0]) else: A = (1j) * A * edge_operator_aij(edge_matrix_indices, stab[i], stab[i + 1]) vac_operator = vac_operator * (QubitOperator(()) + A) / numpy.sqrt(2) return vac_operator
def edge_operator_aij(edge_matrix_indices, i, j): """Calculate the edge operator A_ij. The definitions used here are consistent with arXiv:quant-ph/0003137 Args: edge_matrix_indices(numpy array): specifying the edges i,j (int): specifying the edge operator A Returns: An instance of QubitOperator """ a_ij = QubitOperator() operator = tuple() position_ij = -1 qubit_position_i = numpy.array(numpy.where(edge_matrix_indices == i)) for edge_index in range(numpy.size(edge_matrix_indices[0, :])): if set((i, j)) == set(edge_matrix_indices[:, edge_index]): position_ij = edge_index operator += ((int(position_ij), 'X'),) for edge_index in range(numpy.size(qubit_position_i[0, :])): if edge_matrix_indices[int(not (qubit_position_i[0, edge_index]))][ qubit_position_i[1, edge_index]] < j: operator += ((int(qubit_position_i[1, edge_index]), 'Z'),) qubit_position_j = numpy.array(numpy.where(edge_matrix_indices == j)) for edge_index in range(numpy.size(qubit_position_j[0, :])): if edge_matrix_indices[int(not (qubit_position_j[0, edge_index]))][ qubit_position_j[1, edge_index]] < i: operator += ((int(qubit_position_j[1, edge_index]), 'Z'),) a_ij += QubitOperator(operator, 1) if j < i: a_ij = -1 * a_ij return a_ij
def _transform_ladder_operator(ladder_operator, n_qubits): """ Args: ladder_operator (tuple[int, int]): the ladder operator n_qubits (int): the number of qubits Returns: QubitOperator """ index, action = ladder_operator update_set = _update_set(index, n_qubits) occupation_set = _occupation_set(index) parity_set = _parity_set(index - 1) # Initialize the transformed majorana operator (a_p^\dagger + a_p) / 2 transformed_operator = QubitOperator([(i, 'X') for i in update_set] + [(i, 'Z') for i in parity_set], .5) # Get the transformed (a_p^\dagger - a_p) / 2 # Below is equivalent to X(update_set) * Z(parity_set ^ occupation_set) transformed_majorana_difference = QubitOperator( [(index, 'Y')] + [(i, 'X') for i in update_set - {index}] + [(i, 'Z') for i in (parity_set ^ occupation_set) - {index}], -.5j) # Raising if action == 1: transformed_operator += transformed_majorana_difference # Lowering else: transformed_operator -= transformed_majorana_difference return transformed_operator
def number_operator(iop, mode_number=None): """Find the qubit operator for the number operator in bravyi_kitaev_fast representation Args: iop (InteractionOperator): mode_number: index mode_number corresponding to the mode for which number operator is required. Return: A QubitOperator """ n_qubit = iop.n_qubits num_operator = QubitOperator() edge_matrix = bravyi_kitaev_fast_edge_matrix(iop) edge_matrix_indices = numpy.array( numpy.nonzero( numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix)))) if mode_number is None: for i in range(n_qubit): num_operator += (QubitOperator( ()) - edge_operator_b(edge_matrix_indices, i)) / 2. else: num_operator += (QubitOperator( ()) - edge_operator_b(edge_matrix_indices, mode_number)) / 2. return num_operator
def jordan_wigner_one_body(p, q, coefficient=1.): r"""Map the term a^\dagger_p a_q + h.c. to QubitOperator. Note that the diagonal terms are divided by a factor of 2 because they are equal to their own Hermitian conjugate. """ # Handle off-diagonal terms. qubit_operator = QubitOperator() if p != q: if p > q: p, q = q, p coefficient = coefficient.conjugate() parity_string = tuple((z, 'Z') for z in range(p + 1, q)) for c, (op_a, op_b) in [(coefficient.real, 'XX'), (coefficient.real, 'YY'), (coefficient.imag, 'YX'), (-coefficient.imag, 'XY')]: operators = ((p, op_a),) + parity_string + ((q, op_b),) qubit_operator += QubitOperator(operators, .5 * c) # Handle diagonal terms. else: qubit_operator += QubitOperator((), .5 * coefficient) qubit_operator += QubitOperator(((p, 'Z'),), -.5 * coefficient) return qubit_operator
def test_3rd_order_helper_3ops(self): # Test 3rd-order helper, H=A+B+C op_a = QubitOperator('X0', 1.) op_b = QubitOperator('Z2', 1.) op_c = QubitOperator('Z3', 1.) gold = [] gold.append(op_a * 7. / 24) gold.append(op_b * 7. / 36) gold.append(op_c * 4. / 9) gold.append(op_b * 1. / 2) gold.append(op_c * -4. / 9) gold.append(op_b * -1. / 36) gold.append(op_c * 2. / 3) gold.append(op_a * 3. / 4) gold.append(op_b * -7. / 36) gold.append(op_c * -4. / 9) gold.append(op_b * -1. / 2) gold.append(op_c * 4. / 9) gold.append(op_b * 1. / 36) gold.append(op_c * -2. / 3) gold.append(op_a * -1. / 24) gold.append(op_b * 7. / 24) gold.append(op_c * 2. / 3) gold.append(op_b * 3. / 4) gold.append(op_c * -2. / 3) gold.append(op_b * -1. / 24) gold.append(op_c * 1.0) # Second arg must be in list form res = _third_order_trotter_helper([op_a, op_b, op_c]) # Assert each term in list of QubitOperators is correct self.compare_qubop_lists(gold, res)
def test_trott_ordering_3rd_ord(self): # Test 3rd-order Trotterization, H=A+B+C op_a = QubitOperator('X0', 1.) op_b = QubitOperator('Z2', 1.) op_c = QubitOperator('Z3', 1.) ham = op_a + op_b + op_c # Result from code res = [op for op in trotter_operator_grouping(ham, trotter_order=3)] gold = [] gold.append(op_a * 7. / 24) gold.append(op_b * 7. / 36) gold.append(op_c * 4. / 9) gold.append(op_b * 1. / 2) gold.append(op_c * -4. / 9) gold.append(op_b * -1. / 36) gold.append(op_c * 2. / 3) gold.append(op_a * 3. / 4) gold.append(op_b * -7. / 36) gold.append(op_c * -4. / 9) gold.append(op_b * -1. / 2) gold.append(op_c * 4. / 9) gold.append(op_b * 1. / 36) gold.append(op_c * -2. / 3) gold.append(op_a * -1. / 24) gold.append(op_b * 7. / 24) gold.append(op_c * 2. / 3) gold.append(op_b * 3. / 4) gold.append(op_c * -2. / 3) gold.append(op_b * -1. / 24) gold.append(op_c * 1.0) # Assert each term in list of QubitOperators is correct self.compare_qubop_lists(gold, res)
def one_body(edge_matrix_indices, p, q): r""" Map the term a^\dagger_p a_q + a^\dagger_q a_p to QubitOperator. The definitions for various operators will be presented in a paper soon. Args: edge_matrix_indices(numpy array): Specifying the edges p and q (int): specifying the one body term. Return: An instance of QubitOperator() """ # Handle off-diagonal terms. qubit_operator = QubitOperator() if p != q: a, b = sorted([p, q]) B_a = edge_operator_b(edge_matrix_indices, a) B_b = edge_operator_b(edge_matrix_indices, b) A_ab = edge_operator_aij(edge_matrix_indices, a, b) qubit_operator += -1j / 2. * (A_ab * B_b + B_a * A_ab) # Handle diagonal terms. else: B_p = edge_operator_b(edge_matrix_indices, p) qubit_operator += (QubitOperator((), 1) - B_p) / 2. return qubit_operator
def load_operator(file_name=None, data_directory=None, plain_text=False): """Load FermionOperator or QubitOperator from file. Args: file_name: The name of the saved file. data_directory: Optional data directory to change from default data directory specified in config file. plain_text: Whether the input file is plain text Returns: operator: The stored FermionOperator, BosonOperator, QuadOperator, or QubitOperator Raises: TypeError: Operator of invalid type. """ file_path = get_file_path(file_name, data_directory) if plain_text: with open(file_path, 'r') as f: data = f.read() operator_type, operator_terms = data.split(":\n") if operator_type == 'FermionOperator': operator = FermionOperator(operator_terms) elif operator_type == 'BosonOperator': operator = BosonOperator(operator_terms) elif operator_type == 'QubitOperator': operator = QubitOperator(operator_terms) elif operator_type == 'QuadOperator': operator = QuadOperator(operator_terms) else: raise TypeError('Operator of invalid type.') else: with open(file_path, 'rb') as f: data = marshal.load(f) operator_type = data[0] operator_terms = data[1] if operator_type == 'FermionOperator': operator = FermionOperator() for term in operator_terms: operator += FermionOperator(term, operator_terms[term]) elif operator_type == 'BosonOperator': operator = BosonOperator() for term in operator_terms: operator += BosonOperator(term, operator_terms[term]) elif operator_type == 'QubitOperator': operator = QubitOperator() for term in operator_terms: operator += QubitOperator(term, operator_terms[term]) elif operator_type == 'QuadOperator': operator = QuadOperator() for term in operator_terms: operator += QuadOperator(term, operator_terms[term]) else: raise TypeError('Operator of invalid type.') return operator
def test_rotation(self): qop = QubitOperator('X0 X1', 1) qop += QubitOperator('Z0 Z1', 1) rot_op = QubitOperator('Z1', 1) rotated_qop = rotate_qubit_by_pauli(qop, rot_op, numpy.pi / 4) comp_op = QubitOperator('Z0 Z1', 1) comp_op += QubitOperator('X0 Y1', 1) self.assertEqual(comp_op, rotated_qop)
def test_error_operator_all_diagonal(self): terms = [ QubitOperator(()), QubitOperator('Z0 Z1 Z2'), QubitOperator('Z0 Z3'), QubitOperator('Z0 Z1 Z2 Z3') ] zero = QubitOperator() self.assertTrue(zero == error_operator(terms))
def test_zero(self): n_qubits = 5 transmed_i = reverse_jordan_wigner(QubitOperator(), n_qubits) expected_i = FermionOperator() self.assertTrue(transmed_i == expected_i) retransmed_i = jordan_wigner(transmed_i) expected_i = QubitOperator() self.assertTrue(expected_i == retransmed_i)
def test_fix_single_term(self): """Test fix_single_term function.""" stab2 = QubitOperator('Z1 Z3', -1.0) test_term = QubitOperator('Z1 Z2') fix1 = fix_single_term(test_term, 1, 'Z', 'X', stab2) fix2 = fix_single_term(test_term, 0, 'X', 'X', stab2) self.assertTrue(fix1 == (test_term * stab2)) self.assertTrue(fix2 == test_term)
def test_error_operator_xyz(self): terms = [QubitOperator('X1'), QubitOperator('Y1'), QubitOperator('Z1')] expected = numpy.array([[-2. / 3, 1. / 3 + 1.j / 6, 0., 0.], [1. / 3 - 1.j / 6, 2. / 3, 0., 0.], [0., 0., -2. / 3, 1. / 3 + 1.j / 6], [0., 0., 1. / 3 - 1.j / 6, 2. / 3]]) sparse_op = get_sparse_operator(error_operator(terms)) matrix = sparse_op.todense() self.assertTrue(numpy.allclose(matrix, expected), ("Got " + str(matrix)))
def _qubit_operator_creation(operators, coefficents): """ Takes a list of tuples for operators/indices, and another for coefficents""" qubit_operator = QubitOperator() for index in zip(operators, coefficents): qubit_operator += QubitOperator(index[0], index[1]) return qubit_operator
def test_reduce_terms(self): """Test reduce_terms function using LiH Hamiltonian.""" hamiltonian, spectrum = lih_hamiltonian() qubit_hamiltonian = jordan_wigner(hamiltonian) stab1 = QubitOperator('Z0 Z2', -1.0) stab2 = QubitOperator('Z1 Z3', -1.0) red_eigenspectrum = eigenspectrum( reduce_number_of_terms(qubit_hamiltonian, stab1 + stab2)) self.assertAlmostEqual(spectrum[0], red_eigenspectrum[0])
def test_transm_raise1(self): raising = jordan_wigner(FermionOperator(((1, 1), ))) correct_operators_x = ((0, 'Z'), (1, 'X')) correct_operators_y = ((0, 'Z'), (1, 'Y')) qtermx = QubitOperator(correct_operators_x, 0.5) qtermy = QubitOperator(correct_operators_y, -0.5j) self.assertEqual(raising.terms[correct_operators_x], 0.5) self.assertEqual(raising.terms[correct_operators_y], -0.5j) self.assertTrue(raising == qtermx + qtermy)
def test_transm_lower0(self): lowering = jordan_wigner(FermionOperator(((0, 0), ))) correct_operators_x = ((0, 'X'), ) correct_operators_y = ((0, 'Y'), ) qtermx = QubitOperator(correct_operators_x, 0.5) qtermy = QubitOperator(correct_operators_y, 0.5j) self.assertEqual(lowering.terms[correct_operators_x], 0.5) self.assertEqual(lowering.terms[correct_operators_y], 0.5j) self.assertTrue(lowering == qtermx + qtermy)
def _jordan_wigner_majorana_operator(operator): transformed_operator = QubitOperator() for term, coeff in operator.terms.items(): transformed_term = QubitOperator((), coeff) for majorana_index in term: q, b = divmod(majorana_index, 2) z_string = tuple((i, 'Z') for i in range(q)) bit_flip_op = 'Y' if b else 'X' transformed_term *= QubitOperator(z_string + ((q, bit_flip_op),)) transformed_operator += transformed_term return transformed_operator
def project_onto_sector(operator, qubits, sectors): """ Remove qubit by projecting onto sector. Takes a QubitOperator, and projects out a list of qubits, into either the +1 or -1 sector. Note - this requires knowledge of which sector we wish to project into. Args: operator: the QubitOperator to work on qubits: a list of indices of qubits in operator to remove sectors: for each qubit, whether to project into the 0 subspace (<Z>=1) or the 1 subspace (<Z>=-1). Returns: projected_operator: the resultant operator Raises: TypeError: operator must be a QubitOperator. TypeError: qubits and sector must be an array-like. ValueError: If qubits and sectors have different length. ValueError: If sector are not specified as 0 or 1. """ if not isinstance(operator, QubitOperator): raise TypeError('Input operator must be a QubitOperator.') if not isinstance(qubits, (list, numpy.ndarray)): raise TypeError('Qubit input must be an array-like.') if not isinstance(sectors, (list, numpy.ndarray)): raise TypeError('Sector input must be an array-like.') if len(qubits) != len(sectors): raise ValueError('Number of qubits and sectors must be equal.') for i in sectors: if i not in [0, 1]: raise ValueError('Sectors must be 0 or 1.') projected_operator = QubitOperator() for term, factor in operator.terms.items(): # Any term containing X or Y on the removed # qubits has an expectation value of zero if [t for t in term if t[0] in qubits and t[1] in ['X', 'Y']]: continue new_term = tuple((t[0] - len([q for q in qubits if q < t[0]]), t[1]) for t in term if t[0] not in qubits) new_factor =\ factor * (-1)**(sum([sectors[qubits.index(t[0])] for t in term if t[0] in qubits])) projected_operator += QubitOperator(new_term, new_factor) return projected_operator
def test_exception_Pauli(self): qop = QubitOperator('X0 X1', 1) qop += QubitOperator('Z0 Z1', 1) rot_op = QubitOperator('Z1', 1) rot_op += QubitOperator('Z0', 1) rot_op2 = QubitOperator('Z1', 1) ferm_op = FermionOperator('1^ 2', 1) with self.assertRaises(TypeError): rotate_qubit_by_pauli(qop, rot_op, numpy.pi / 4) with self.assertRaises(TypeError): rotate_qubit_by_pauli(ferm_op, rot_op2, numpy.pi / 4)
def test_transform(self): code = BinaryCode([[1, 0, 0], [0, 1, 0]], ['W0', 'W1', '1 + W0 + W1']) hamiltonian = FermionOperator('0^ 2', 0.5) + FermionOperator( '2^ 0', 0.5) transform = binary_code_transform(hamiltonian, code) correct_op = QubitOperator('X0 Z1', 0.25) + QubitOperator('X0', 0.25) self.assertTrue(transform == correct_op) with self.assertRaises(TypeError): binary_code_transform('0^ 2', code) with self.assertRaises(TypeError): binary_code_transform(hamiltonian, ([[1, 0], [0, 1]], ['w0', 'w1']))
def test_lookup_term(self): """Test for the auxiliar function _lookup_term.""" # Dummy test where the initial Pauli string is larger than the # updated one. start_op = list(QubitOperator('Z0 Z1 Z2 Z3').terms.keys())[0] updateop1 = QubitOperator('Z0 Z2', -1.0) updateop2 = list(QubitOperator('Z0 Z1 Z2 Z3').terms.keys()) qop = _lookup_term(start_op, [updateop1], updateop2) final_op = list(qop.terms.keys())[0] self.assertLess(len(final_op), len(start_op))
def test_projection_error(self): coefficient = 0.5 opstring = ((0, 'X'), (1, 'X'), (2, 'Z')) opstring2 = ((0, 'X'), (2, 'Z'), (3, 'Z')) operator = QubitOperator(opstring, coefficient) operator += QubitOperator(opstring2, coefficient) new_operator = project_onto_sector(operator, qubits=[1], sectors=[0]) error = projection_error(operator, qubits=[1], sectors=[0]) self.assertEqual(count_qubits(new_operator), 3) self.assertTrue(((0, 'X'), (1, 'Z'), (2, 'Z')) in new_operator.terms) self.assertEqual(new_operator.terms[((0, 'X'), (1, 'Z'), (2, 'Z'))], 0.5) self.assertEqual(error, 0.5)
def test_transm_raise3_sympy(self): coeff = sympy.Symbol('x') raising = jordan_wigner(coeff * FermionOperator(((3, 1), ))) self.assertEqual(len(raising.terms), 2) correct_operators_x = ((0, 'Z'), (1, 'Z'), (2, 'Z'), (3, 'X')) correct_operators_y = ((0, 'Z'), (1, 'Z'), (2, 'Z'), (3, 'Y')) qtermx = QubitOperator(correct_operators_x, 0.5 * coeff) qtermy = QubitOperator(correct_operators_y, -0.5j * coeff) self.assertEqual(raising.terms[correct_operators_x], 0.5 * coeff) self.assertEqual(raising.terms[correct_operators_y], -0.5j * coeff) self.assertTrue(raising == qtermx + qtermy)
def test_trott_ordering_2nd_ord(self): # Test 2nd-order Trotter ordering op_a = QubitOperator('X0', 1.) op_b = QubitOperator('Z2', 1.) op_c = QubitOperator('Z3', 1.) ham = op_a + op_b + op_c _ = [op for op in trotter_operator_grouping(ham, trotter_order=2)] gold = [] gold.append(op_a * 0.5) gold.append(op_b * 0.5) gold.append(op_c * 1.0) gold.append(op_b * 0.5) gold.append(op_a * 0.5)
def test_matvec_multiple_terms(self): """Testing with multiple terms.""" qubit_operator = (QubitOperator.identity() + 2 * QubitOperator('Y2') + QubitOperator(((0, 'Z'), (1, 'X')), 10.0)) vec = numpy.array([1, 2, 3, 4, 5, 6, 7, 8]) matvec_expected = (10 * numpy.array([3, 4, 1, 2, -7, -8, -5, -6]) + 2j * numpy.array([-2, 1, -4, 3, -6, 5, -8, 7]) + vec) self.assertTrue( numpy.allclose( LinearQubitOperator(qubit_operator) * vec, matvec_expected))
def test_reduce_terms_manual_input(self): """Test reduce_terms function using LiH Hamiltonian.""" hamiltonian, spectrum = lih_hamiltonian() qubit_hamiltonian = jordan_wigner(hamiltonian) stab1 = QubitOperator('Z0 Z2', -1.0) stab2 = QubitOperator('Z1 Z3', -1.0) red_eigenspectrum = eigenspectrum( reduce_number_of_terms(qubit_hamiltonian, [stab1, stab2], manual_input=True, fixed_positions=[0, 1])) self.assertAlmostEqual(spectrum[0], red_eigenspectrum[0])
def test_exceptions(self): # Test exceptions in trotter_operator_grouping() with self.assertRaises(TypeError): _ = [ i for i in pauli_exp_to_qasm([QubitOperator()], qubit_list='qubit') ] with self.assertRaises(TypeError): _ = [ i for i in pauli_exp_to_qasm([QubitOperator('X1 Y2')], qubit_list=['qubit']) ] with self.assertRaises(ValueError): _ = [ i for i in trotter_operator_grouping(self.qo1, trotter_order=0) ] with self.assertRaises(ValueError): _ = [ i for i in trotter_operator_grouping(self.qo1, trotter_order=4) ] with self.assertRaises(TypeError): _ = [i for i in trotter_operator_grouping(42)] emptyQO = QubitOperator() with self.assertRaises(TypeError): _ = [i for i in trotter_operator_grouping(emptyQO)] emptyTO = [] with self.assertRaises(TypeError): _ = [ i for i in trotter_operator_grouping(self.qo1, term_ordering=emptyTO) ] # Too few ops for 2nd-order with self.assertRaises(ValueError): _ = [ i for i in trotter_operator_grouping(self.opA, trotter_order=2) ] # Too few ops for 3rd-order with self.assertRaises(ValueError): _ = [ i for i in trotter_operator_grouping(self.opA, trotter_order=3) ]