def _jordan_wigner_interaction_op(iop, n_qubits=None): """Output InteractionOperator as QubitOperator class under JW transform. One could accomplish this very easily by first mapping to fermions and then mapping to qubits. We skip the middle step for the sake of speed. This only works for real InteractionOperators (no complex numbers). Returns: qubit_operator: An instance of the QubitOperator class. """ if n_qubits is None: n_qubits = count_qubits(iop) if n_qubits < count_qubits(iop): raise ValueError('Invalid number of qubits specified.') # Initialize qubit operator as constant. qubit_operator = QubitOperator((), iop.constant) # Transform diagonal one-body terms for p in range(n_qubits): coefficient = iop[(p, 1), (p, 0)] qubit_operator += jordan_wigner_one_body(p, p, coefficient) # Transform other one-body terms and "diagonal" two-body terms for p, q in itertools.combinations(range(n_qubits), 2): # One-body coefficient = .5 * (iop[(p, 1), (q, 0)] + iop[(q, 1), (p, 0)].conjugate()) qubit_operator += jordan_wigner_one_body(p, q, coefficient) # Two-body coefficient = (iop[(p, 1), (q, 1), (p, 0), (q, 0)] - iop[(p, 1), (q, 1), (q, 0), (p, 0)] - iop[(q, 1), (p, 1), (p, 0), (q, 0)] + iop[(q, 1), (p, 1), (q, 0), (p, 0)]) qubit_operator += jordan_wigner_two_body(p, q, p, q, coefficient) # Transform the rest of the two-body terms for (p, q), (r, s) in itertools.combinations( itertools.combinations(range(n_qubits), 2), 2): coefficient = 0.5 * (iop[(p, 1), (q, 1), (r, 0), (s, 0)] + iop[(s, 1), (r, 1), (q, 0), (p, 0)].conjugate() - iop[(p, 1), (q, 1), (s, 0), (r, 0)] - iop[(r, 1), (s, 1), (q, 0), (p, 0)].conjugate() - iop[(q, 1), (p, 1), (r, 0), (s, 0)] - iop[(s, 1), (r, 1), (p, 0), (q, 0)].conjugate() + iop[(q, 1), (p, 1), (s, 0), (r, 0)] + iop[(r, 1), (s, 1), (p, 0), (q, 0)].conjugate()) qubit_operator += jordan_wigner_two_body(p, q, r, s, coefficient) return qubit_operator
def test_jellium_hamiltonian_correctly_broken_up(self): grid = Grid(2, 3, 1.) hamiltonian = jellium_model(grid, spinless=True, plane_wave=False) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian) ) potential = sum(potential_terms, FermionOperator.zero()) kinetic = sum(kinetic_terms, FermionOperator.zero()) true_potential = dual_basis_jellium_model(grid, spinless=True, kinetic=False) true_kinetic = dual_basis_jellium_model(grid, spinless=True, potential=False) for i in range(count_qubits(true_kinetic)): coeff = true_kinetic.terms.get(((i, 1), (i, 0))) if coeff: true_kinetic -= FermionOperator(((i, 1), (i, 0)), coeff) true_potential += FermionOperator(((i, 1), (i, 0)), coeff) self.assertEqual(potential, true_potential) self.assertEqual(kinetic, true_kinetic)
def qubit_operator_to_pauli_sum( operator: QubitOperator, qubits: Optional[Sequence[cirq.Qid]] = None) -> cirq.PauliSum: """ Convert QubitOperator to a sum of PauliString. Args: operator (QubitOperator): operator to convert. qubits (List): Optional list of qubit names. If `None` a list of `cirq.LineQubit` of length number of qubits in operator is created. Returns: pauli_sum (PauliSum): cirq PauliSum object. Raises: TypeError: if qubit_op is not a QubitOpertor. """ if not isinstance(operator, QubitOperator): raise TypeError('Input must be a QubitOperator.') if qubits is None: qubits = cirq.LineQubit.range(count_qubits(operator)) pauli_sum = cirq.PauliSum() for pauli in operator.terms.items(): pauli_sum += _qubit_operator_term_to_pauli_string(pauli, qubits) return pauli_sum
def _jordan_wigner_diagonal_coulomb_hamiltonian(operator): n_qubits = count_qubits(operator) qubit_operator = QubitOperator((), operator.constant) # Transform diagonal one-body terms for p in range(n_qubits): coefficient = operator.one_body[p, p] + operator.two_body[p, p] qubit_operator += QubitOperator(((p, 'Z'),), -.5 * coefficient) qubit_operator += QubitOperator((), .5 * coefficient) # Transform other one-body terms and two-body terms for p, q in itertools.combinations(range(n_qubits), 2): # One-body real_part = numpy.real(operator.one_body[p, q]) imag_part = numpy.imag(operator.one_body[p, q]) parity_string = [(i, 'Z') for i in range(p + 1, q)] qubit_operator += QubitOperator([(p, 'X')] + parity_string + [(q, 'X')], .5 * real_part) qubit_operator += QubitOperator([(p, 'Y')] + parity_string + [(q, 'Y')], .5 * real_part) qubit_operator += QubitOperator([(p, 'Y')] + parity_string + [(q, 'X')], .5 * imag_part) qubit_operator += QubitOperator([(p, 'X')] + parity_string + [(q, 'Y')], -.5 * imag_part) # Two-body coefficient = operator.two_body[p, q] qubit_operator += QubitOperator(((p, 'Z'), (q, 'Z')), .5 * coefficient) qubit_operator += QubitOperator((p, 'Z'), -.5 * coefficient) qubit_operator += QubitOperator((q, 'Z'), -.5 * coefficient) qubit_operator += QubitOperator((), .5 * coefficient) return qubit_operator
def get_interaction_rdm(qubit_operator, n_qubits=None): """Build an InteractionRDM from measured qubit operators. Returns: An InteractionRDM object. """ check_no_sympy(qubit_operator) # Avoid circular import. from openfermion.transforms import jordan_wigner if n_qubits is None: n_qubits = count_qubits(qubit_operator) one_rdm = numpy.zeros((n_qubits, ) * 2, dtype=complex) two_rdm = numpy.zeros((n_qubits, ) * 4, dtype=complex) # One-RDM. for i, j in itertools.product(range(n_qubits), repeat=2): transformed_operator = jordan_wigner(FermionOperator(((i, 1), (j, 0)))) for term, coefficient in transformed_operator.terms.items(): if term in qubit_operator.terms: one_rdm[i, j] += coefficient * qubit_operator.terms[term] # Two-RDM. for i, j, k, l in itertools.product(range(n_qubits), repeat=4): transformed_operator = jordan_wigner( FermionOperator(((i, 1), (j, 1), (k, 0), (l, 0)))) for term, coefficient in transformed_operator.terms.items(): if term in qubit_operator.terms: two_rdm[i, j, k, l] += coefficient * qubit_operator.terms[term] return InteractionRDM(one_rdm, two_rdm)
def test_qasm_string_Controlled_XYZ(self): # Testing for correct QASM string output w/ Pauli-{X,Y,Z} # QubitOperator('X0 Z1 Y3', 0.5) and a controlled ancilla # Number of qubits qasmstr = str(count_qubits(self.opA) + 1) + "\n" # Write each QASM operation qasmstr += "\n".join( trotterize_exp_qubop_to_qasm(self.opA, ancilla='ancilla')) # Correct string strcorrect = '''5 H 0 Rx 1.5707963267948966 3 CNOT 0 1 CNOT 1 3 C-Phase -1.0 ancilla 3 Rz 0.5 ancilla CNOT 1 3 CNOT 0 1 H 0 Rx -1.5707963267948966 3''' self.assertEqual(qasmstr, strcorrect)
def test_qasm_string_Controlled_XYZ_ancilla_list(self): # Testing for correct QASM string output w/ Pauli-{X,Y,Z} # QubitOperator('X0 Z1 Y3', 0.5) and a controlled ancilla # Number of qubits qasmstr = str(count_qubits(self.opA) + 1) + "\n" qubit_list = ['q0', 'q1', 'q2', 'q3'] # Write each QASM operation qasmstr += "\n".join( trotterize_exp_qubop_to_qasm(self.opA, ancilla='ancilla', qubit_list=qubit_list)) # Correct string strcorrect = '''5 H q0 Rx 1.5707963267948966 q3 CNOT q0 q1 CNOT q1 q3 C-Phase -1.0 ancilla q3 Rz 0.5 ancilla CNOT q1 q3 CNOT q0 q1 H q0 Rx -1.5707963267948966 q3''' self.assertEqual(qasmstr, strcorrect)
def test_qasm_string_identity(self): qasmstr = str(count_qubits(self.op_id) + 1) + "\n" # Write each QASM operation qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.op_id)) strcorrect = '''1\n''' self.assertEqual(qasmstr, strcorrect)
def _diagonal_coulomb_hamiltonian_to_fermion_operator(operator): fermion_operator = FermionOperator() n_qubits = op_utils.count_qubits(operator) fermion_operator += FermionOperator((), operator.constant) for p, q in itertools.product(range(n_qubits), repeat=2): fermion_operator += FermionOperator(((p, 1), (q, 0)), operator.one_body[p, q]) fermion_operator += FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)), operator.two_body[p, q]) return fermion_operator
def test_case_one_body_op_success(self): # Case A: Simplest class of operators # (Number operators and Excitation operators) for i in range(self.test_range): for j in range(i): ham = self.two_op(i, j) + self.two_op(j, i) n_qubits = count_qubits(ham) opf_ham = bravyi_kitaev(ham, n_qubits) custom = bravyi_kitaev(get_interaction_operator(ham)) assert custom == opf_ham
def _bravyi_kitaev_fermion_operator(operator, n_qubits): # Compute the number of qubits. N = count_qubits(operator) if n_qubits is None: n_qubits = N if n_qubits < N: raise ValueError('Invalid number of qubits specified.') # Compute transformed operator. transformed_terms = (_transform_operator_term( term=term, coefficient=operator.terms[term], n_qubits=n_qubits) for term in operator.terms) return inline_sum(summands=transformed_terms, seed=QubitOperator())
def test_sparse_numerically(self): # Check FCI energy. energy, wavefunction = get_ground_state(self.hamiltonian_matrix) self.assertAlmostEqual(energy, self.molecule.fci_energy) expected_energy = expectation(self.hamiltonian_matrix, wavefunction) self.assertAlmostEqual(expected_energy, energy) # Make sure you can reproduce Hartree energy. hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) hf_density = get_density_matrix([hf_state], [1.]) # Make sure you can reproduce Hartree-Fock energy. hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) hf_density = get_density_matrix([hf_state], [1.]) expected_hf_density_energy = expectation(self.hamiltonian_matrix, hf_density) expected_hf_energy = expectation(self.hamiltonian_matrix, hf_state) self.assertAlmostEqual(expected_hf_energy, self.molecule.hf_energy) self.assertAlmostEqual(expected_hf_density_energy, self.molecule.hf_energy)
def _bravyi_kitaev_majorana_operator(operator, n_qubits): # Compute the number of qubits. N = op_utils.count_qubits(operator) if n_qubits is None: n_qubits = N if n_qubits < N: raise ValueError('Invalid number of qubits specified.') # Compute transformed operator. transformed_terms = (_transform_majorana_term(term=term, coefficient=coeff, n_qubits=n_qubits) for term, coeff in operator.terms.items()) return op_utils.inline_sum(summands=transformed_terms, seed=QubitOperator())
def test_qasm_string_Z(self): # Testing for correct QASM string output w/ Pauli-X # Number of qubits qasmstr = str(count_qubits(self.opB)) + "\n" # Write each QASM operation qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.opB)) # Correct string strcorrect = '''5 CNOT 3 4 Rz 0.6 4 CNOT 3 4''' self.assertEqual(qasmstr, strcorrect)
def test_double_excitation_op_success(self): # Case D: Double-excitation operator for i in range(self.test_range): for j in range(self.test_range): for k in range(self.test_range): for l in range(self.test_range): if len({i, j, k, l}) == 4: print(i, j, k, l) ham = self.four_op(i, j, k, l) + self.four_op( k, l, i, j) n_qubits = count_qubits(ham) opf_ham = bravyi_kitaev(ham, n_qubits) custom = bravyi_kitaev( get_interaction_operator(ham)) assert custom == opf_ham
def test_qasm_string_multiple_operator(self): # Testing for correct QASM string output # w/ sum of Pauli-{X,Y,Z} and Pauli-{Z} # QubitOperator('X0 Z1 Y3', 0.5) + QubitOperator('Z3 Z4', 0.6) # Number of qubits qasmstr = str(count_qubits(self.qo1)) + "\n" # Write each QASM operation qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.qo1)) # Two possibilities for the correct string depending on # the order in which the operators loop. strcorrect1 = '''5 CNOT 3 4 Rz 0.6 4 CNOT 3 4 H 0 Rx 1.5707963267948966 3 CNOT 0 1 CNOT 1 3 Rz 0.5 3 CNOT 1 3 CNOT 0 1 H 0 Rx -1.5707963267948966 3''' strcorrect2 = '''5 H 0 Rx 1.5707963267948966 3 CNOT 0 1 CNOT 1 3 Rz 0.5 3 CNOT 1 3 CNOT 0 1 H 0 Rx -1.5707963267948966 3 CNOT 3 4 Rz 0.6 4 CNOT 3 4''' try: self.assertEqual(qasmstr, strcorrect1) except: self.assertEqual(qasmstr, strcorrect2)
def __init__(self, qubit_operator, n_qubits=None): """ Args: qubit_operator(QubitOperator): A qubit operator to be applied on vectors. n_qubits(int): The total number of qubits """ calculated_n_qubits = op_utils.count_qubits(qubit_operator) if n_qubits is None: n_qubits = calculated_n_qubits elif n_qubits < calculated_n_qubits: raise ValueError('Invalid number of qubits specified ' '{} < {}.'.format(n_qubits, calculated_n_qubits)) n_hilbert = 2**n_qubits super(LinearQubitOperator, self).__init__(shape=(n_hilbert, n_hilbert), dtype=complex) self.qubit_operator = qubit_operator self.n_qubits = n_qubits
def test_qasm_string_XYZ(self): # Testing for correct QASM string output w/ Pauli-{X,Y,Z} # QubitOperator('X0 Z1 Y3', 0.5) # Number of qubits qasmstr = str(count_qubits(self.opA)) + "\n" # Write each QASM operation qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.opA)) # Correct string strcorrect = '''4 H 0 Rx 1.5707963267948966 3 CNOT 0 1 CNOT 1 3 Rz 0.5 3 CNOT 1 3 CNOT 0 1 H 0 Rx -1.5707963267948966 3''' self.assertEqual(qasmstr, strcorrect)
def __init__(self, qubit_operator, n_qubits=None, options=None): """ Args: qubit_operator(QubitOperator): A qubit operator to be applied on vectors. n_qubits(int): The total number of qubits options(LinearQubitOperatorOptions): Options for the LinearOperator. """ n_qubits = n_qubits or op_utils.count_qubits(qubit_operator) n_hilbert = 2**n_qubits super(ParallelLinearQubitOperator, self).__init__(shape=(n_hilbert, n_hilbert), dtype=complex) self.qubit_operator = qubit_operator self.n_qubits = n_qubits self.options = options or LinearQubitOperatorOptions() self.qubit_operator_groups = list( qubit_operator.get_operator_groups(self.options.processes)) self.linear_operators = [ LinearQubitOperator(operator, n_qubits) for operator in self.qubit_operator_groups ]
def bit_mask_of_modes_acted_on_by_fermionic_terms(fermion_term_list, n_qubits=None): """Create a mask of which modes of the system are acted on by which terms. Args: fermion_term_list (list of FermionOperators): A list of fermionic terms to calculate the bitmask for. n_qubits (int): The number of qubits (modes) in the system. If not specified, defaults to the maximum of any term in fermion_term_list. Returns: An n_qubits x len(fermion_term_list) boolean numpy array of whether each term acts on the given mode index. Raises: ValueError: if n_qubits is too small for the given terms. """ if n_qubits is None: n_qubits = 0 for term in fermion_term_list: n_qubits = max(n_qubits, count_qubits(term)) mask = numpy.zeros((n_qubits, len(fermion_term_list)), dtype=bool) for term_number, term in enumerate(fermion_term_list): actions = term.terms for action in actions: for single_operator in action: mode = single_operator[0] try: mask[mode][term_number] = True except IndexError: raise ValueError('Bad n_qubits: must be greater than ' 'highest mode in any FermionOperator.') return mask
def test_tappering_stabilizer_more_qubits(self): """Test for stabilizer with more qubits than operator.""" hamiltonian = QubitOperator('Y0 Y1', 1.0) stab = QubitOperator('X0 X1 X2', -1.0) num_qubits = max(count_qubits(hamiltonian), count_qubits(stab)) tap_ham = taper_off_qubits(hamiltonian, stab) num_qubits_tap = count_qubits(tap_ham) self.assertFalse(num_qubits == num_qubits_tap) hamiltonian = QubitOperator('X0 X1', 1.0) stab = QubitOperator('Y0 Y1 Y2', -1.0) num_qubits = max(count_qubits(hamiltonian), count_qubits(stab)) tap_ham = taper_off_qubits(hamiltonian, stab) num_qubits_tap = count_qubits(tap_ham) self.assertFalse(num_qubits == num_qubits_tap)
def test_n_qubits_interaction_operator(self): self.assertEqual(self.n_qubits, count_qubits(self.interaction_operator))
def test_n_qubits_qubit_operator(self): self.assertEqual(self.n_qubits, count_qubits(self.qubit_operator))
def test_n_qubits_fermion_operator(self): self.assertEqual(self.n_qubits, count_qubits(self.fermion_operator))
def test_n_qubits_single_fermion_term(self): self.assertEqual(self.n_qubits, count_qubits(self.fermion_term))
def test_n_qubits_majorana_operator(self): self.assertEqual(self.n_qubits, count_qubits(self.majorana_operator))
def taper_off_qubits(operator, stabilizers, manual_input=False, fixed_positions=None, output_tapered_positions=False): r""" Remove qubits from given operator. Qubits are removed by eliminating an equivalent number of stabilizer conditions. Which qubits that are can either be determined automatically or their positions can be set manually. Qubits can be disregarded from the Hamiltonian when the effect of all its terms on them is rendered trivial. This algorithm employs a stabilizers like :math:`\pm X \otimes p` to fix the action of every Pauli string on the first qubit to :math:`Z` or the identity. A string :math:`X \otimes h` would for instance be multiplied with the stabilizer to obtain :math:`1 \otimes (\pm h\cdot p)` while a string :math:`Z \otimes h^\prime` would pass without correction. The first qubit can subsequently be removed as it must be in the computational basis in Hamiltonian eigenstates. For stabilizers acting as :math:`Y` (:math:`Z`) on selected qubits, the algorithm would fix the action of every Hamiltonian string to :math:`Z` (:math:`X`). Updating also the list of remaining stabilizer generators, the algorithm is run iteratively. Args: operator (QubitOperator): Operator of which qubits will be removed. stabilizers (QubitOperator): Stabilizer generators for the tapering. Can also be passed as a list of QubitOperator. manual_input (Boolean): Option to pass the list of fixed qubits positions manually. Set to False by default. fixed_positions (list): (optional) List of fixed qubit positions. Passing a list is only effective if manual_input is True. output_tapered_positions (Boolean): Option to output the positions of qubits that have been removed. Returns: skimmed_operator (QubitOperator): Operator with fewer qubits. removed_positions (list): (optional) List of removed qubit positions. For the qubits to be gone in the qubit count, the remaining qubits have been moved up to those indices. """ if isinstance(stabilizers, (list, tuple, numpy.ndarray)): n_qbits_stabs = 0 for ent in stabilizers: if op_utils.count_qubits(ent) > n_qbits_stabs: n_qbits_stabs = op_utils.count_qubits(ent) else: n_qbits_stabs = op_utils.count_qubits(stabilizers) n_qbits = max(op_utils.count_qubits(operator), n_qbits_stabs) (ham_to_update, qbts_to_rm) = reduce_number_of_terms(operator, stabilizers, maintain_length=False, manual_input=manual_input, fixed_positions=fixed_positions, output_fixed_positions=True) # Gets a list of the order of the qubits after tapering qbit_order = list(numpy.arange(n_qbits - len(qbts_to_rm), dtype=int)) # Save the original list before it gets ordered removed_positions = qbts_to_rm qbts_to_rm.sort() for x in qbts_to_rm: qbit_order.insert(x, 'remove') # Remove the qubits skimmed_operator = QubitOperator() for term, coef in ham_to_update.terms.items(): if term == (): skimmed_operator += QubitOperator('', coef) continue tap_tpls = [] for p in term: if qbit_order[p[0]] != 'remove': tap_tpls.append((qbit_order[p[0]].item(), p[1])) skimmed_operator += QubitOperator(tuple(tap_tpls), coef) if output_tapered_positions: return skimmed_operator, removed_positions else: return skimmed_operator
def test_n_qubits_bad_type(self): with self.assertRaises(TypeError): count_qubits('twelve')
def test_all(self): # Test reverse Jordan-Wigner. fermion_hamiltonian = reverse_jordan_wigner(self.qubit_hamiltonian) fermion_hamiltonian = normal_ordered(fermion_hamiltonian) self.assertTrue(self.fermion_hamiltonian == fermion_hamiltonian) # Test mapping to interaction operator. fermion_hamiltonian = get_fermion_operator(self.molecular_hamiltonian) fermion_hamiltonian = normal_ordered(fermion_hamiltonian) self.assertTrue(self.fermion_hamiltonian == fermion_hamiltonian) # Test RDM energy. fci_rdm_energy = self.nuclear_repulsion fci_rdm_energy += numpy.sum(self.fci_rdm.one_body_tensor * self.one_body) fci_rdm_energy += numpy.sum(self.fci_rdm.two_body_tensor * self.two_body) self.assertAlmostEqual(fci_rdm_energy, self.molecule.fci_energy) # Confirm expectation on qubit Hamiltonian using reverse JW matches. qubit_rdm = self.fci_rdm.get_qubit_expectations(self.qubit_hamiltonian) qubit_energy = 0.0 for term, coefficient in qubit_rdm.terms.items(): qubit_energy += coefficient * self.qubit_hamiltonian.terms[term] self.assertAlmostEqual(qubit_energy, self.molecule.fci_energy) # Confirm fermionic RDMs can be built from measured qubit RDMs. new_fermi_rdm = get_interaction_rdm(qubit_rdm) new_fermi_rdm.expectation(self.molecular_hamiltonian) self.assertAlmostEqual(fci_rdm_energy, self.molecule.fci_energy) # Test sparse matrices. energy, wavefunction = get_ground_state(self.hamiltonian_matrix) self.assertAlmostEqual(energy, self.molecule.fci_energy) expected_energy = expectation(self.hamiltonian_matrix, wavefunction) self.assertAlmostEqual(expected_energy, energy) # Make sure you can reproduce Hartree-Fock energy. hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) hf_density = get_density_matrix([hf_state], [1.]) expected_hf_density_energy = expectation(self.hamiltonian_matrix, hf_density) expected_hf_energy = expectation(self.hamiltonian_matrix, hf_state) self.assertAlmostEqual(expected_hf_energy, self.molecule.hf_energy) self.assertAlmostEqual(expected_hf_density_energy, self.molecule.hf_energy) # Check that frozen core result matches frozen core FCI from psi4. # Recore frozen core result from external calculation. self.frozen_core_fci_energy = -7.8807607374168 no_core_fci_energy = numpy.linalg.eigh( self.hamiltonian_matrix_no_core.toarray())[0][0] self.assertAlmostEqual(no_core_fci_energy, self.frozen_core_fci_energy) # Check that the freeze_orbitals function has the same effect as the # as the occupied_indices option of get_molecular_hamiltonian. frozen_hamiltonian = freeze_orbitals( get_fermion_operator(self.molecular_hamiltonian), [0, 1]) self.assertTrue(frozen_hamiltonian == get_fermion_operator( self.molecular_hamiltonian_no_core))
def bravyi_kitaev_fast_interaction_op(iop): r""" Transform from InteractionOperator to QubitOperator for Bravyi-Kitaev fast algorithm. The electronic Hamiltonian is represented in terms of creation and annihilation operators. These creation and annihilation operators could be used to define Majorana modes as follows: c_{2i} = a_i + a^{\dagger}_i, c_{2i+1} = (a_i - a^{\dagger}_{i})/(1j) These Majorana modes can be used to define edge operators B_i and A_{ij}: B_i=c_{2i}c_{2i+1}, A_{ij}=c_{2i}c_{2j} using these edge operators the fermionic algebra can be generated and hence all the terms in the electronic Hamiltonian can be expressed in terms of edge operators. The terms in electronic Hamiltonian can be divided into five types (arXiv 1208.5986). We can find the edge operator expression for each of those five types. For example, the excitation operator term in Hamiltonian when represented in terms of edge operators becomes: a_i^{\dagger}a_j+a_j^{\dagger}a_i = (-1j/2)*(A_ij*B_i+B_j*A_ij) For the sake of brevity the reader is encouraged to look up the expressions of other terms from the code below. The variables for edge operators are chosen according to the nomenclature defined above (B_i and A_ij). A detailed description of these operators and the terms of the electronic Hamiltonian are provided in (arXiv 1712.00446). Args: iop (Interaction Operator): n_qubit (int): Number of qubits Returns: qubit_operator: An instance of the QubitOperator class. """ n_qubits = count_qubits(iop) # Initialize qubit operator as constant. qubit_operator = QubitOperator((), iop.constant) 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)))) # Loop through all indices. for p in range(n_qubits): for q in range(n_qubits): # Handle one-body terms. coefficient = complex(iop[(p, 1), (q, 0)]) if coefficient and p >= q: qubit_operator += (coefficient * one_body(edge_matrix_indices, p, q)) # Keep looping for the two-body terms. for r in range(n_qubits): for s in range(n_qubits): coefficient = complex(iop[(p, 1), (q, 1), (r, 0), (s, 0)]) # Skip zero terms. if (not coefficient) or (p == q) or (r == s): # coverage: ignore continue # Identify and skip one of the complex conjugates. if [p, q, r, s] != [s, r, q, p]: if len(set([p, q, r, s])) == 4: if min(r, s) < min(p, q): continue # Handle case of 3 unique indices elif len(set([p, q, r, s])) == 3: transformed_term = two_body(edge_matrix_indices, p, q, r, s) transformed_term *= .5 * coefficient qubit_operator += transformed_term continue elif p != r and q < p: # TODO: remove pragma if reachable continue continue # pragma: no cover # Handle the two-body terms. transformed_term = two_body(edge_matrix_indices, p, q, r, s) transformed_term *= coefficient qubit_operator += transformed_term return qubit_operator