def test_2body_adjacent_number_operator_1D(self): operator = FermionOperator('3^ 2^ 2 1') operator = normal_ordered(operator) transformed_operator = normal_ordered( fourier_transform(operator, self.grid1, self.spinless)) expected = expectation(get_sparse_operator(transformed_operator), self.hf_state1) actual = expectation_db_operator_with_pw_basis_state( operator, self.reversed_occupied_orbitals1, self.n_spatial_orbitals, self.grid1, self.spinless) self.assertAlmostEqual(expected, actual)
def operator_2d_fft_with_reordering(fermion_operator, system_size): """Apply the 2D FFT to an operator after reordering its modes. Args: fermion_operator (projectq.FermionOperator): Original operator. system_size (int): The side length of the system. register must thus have system_size ** 2 qubits. Notes: The reordering of the operators is the opposite of the ordering that would be applied to a state. This reordering is given in two stages. First, the bit-reversed output of the FFFT circuit (e.g. 0, 2, 1, 3 which is the four numbers 0, 1, 2, 3 after reversing their bit representations) is swapped to the standard order (e.g. 0, 1, 2, 3). Second, the standard order is swapped to the split-halves ordering used in fourier_transform (e.g. 2, 3, 0, 1 which swaps the left and right halves of that range). Both rounds must be applied before the FFFT, but after the FFFT only the second round needs to be undone so as to restore to the order of the FFFT circuit. All reorderings must be done in both dimensions, and because the operator is fermionic all swaps must also be. """ fermion_operator = normal_ordered(fermion_operator) grid = Grid(dimensions=2, length=system_size, scale=1.0) first_round_swaps, second_round_swaps = ffft_swap_networks(system_size, n_dimensions=2) # Create the full list of swaps. swap_mode_list = first_round_swaps + second_round_swaps swaps = [swap[0] for swap_layer in swap_mode_list for swap in swap_layer] # Swap adjacent modes for all swaps in both rounds. for mode in swaps: fermion_operator = swap_adjacent_fermionic_modes( fermion_operator, mode) fermion_operator = normal_ordered(fermion_operator) # Apply the Fourier transform to the reordered operator. fft_result = fourier_transform(fermion_operator, grid, spinless=True) fft_result = normal_ordered(fft_result) # Undo the second round of swaps to restore the FFT's bit-reversed order. swap_mode_list = second_round_swaps swaps = [swap[0] for swap_layer in swap_mode_list for swap in swap_layer] for mode in swaps: fft_result = swap_adjacent_fermionic_modes(fft_result, mode) fft_result = normal_ordered(fft_result) return fft_result
def test_fourier_transform(self): grid = Grid(dimensions=1, scale=1.5, length=3) spinless_set = [True, False] geometry = [('H', (0, )), ('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.assertTrue( normal_ordered(h_plane_wave_t).isclose( normal_ordered(h_dual_basis)))
def test_yy(self): yy = QubitOperator(((2, 'Y'), (3, 'Y')), 2.) transmed_yy = reverse_jordan_wigner(yy) retransmed_yy = jordan_wigner(transmed_yy) expected1 = -(FermionOperator(((2, 1), ), 2.) + FermionOperator( ((2, 0), ), 2.)) expected2 = (FermionOperator(((3, 1), )) - FermionOperator(((3, 0), ))) expected = expected1 * expected2 self.assertTrue(yy.isclose(retransmed_yy)) self.assertTrue( normal_ordered(transmed_yy).isclose(normal_ordered(expected)))
def test_xy(self): xy = QubitOperator(((4, 'X'), (5, 'Y')), -2.j) transmed_xy = reverse_jordan_wigner(xy) retransmed_xy = jordan_wigner(transmed_xy) expected1 = -2j * (FermionOperator(((4, 1), ), 1j) - FermionOperator( ((4, 0), ), 1j)) expected2 = (FermionOperator(((5, 1), )) - FermionOperator(((5, 0), ))) expected = expected1 * expected2 self.assertTrue(xy.isclose(retransmed_xy)) self.assertTrue( normal_ordered(transmed_xy).isclose(normal_ordered(expected)))
def test_xx(self): xx = QubitOperator(((3, 'X'), (4, 'X')), 2.) transmed_xx = reverse_jordan_wigner(xx) retransmed_xx = jordan_wigner(transmed_xx) expected1 = (FermionOperator(((3, 1), ), 2.) - FermionOperator( ((3, 0), ), 2.)) expected2 = (FermionOperator(((4, 1), ), 1.) + FermionOperator( ((4, 0), ), 1.)) expected = expected1 * expected2 self.assertTrue(xx.isclose(retransmed_xx)) self.assertTrue( normal_ordered(transmed_xx).isclose(normal_ordered(expected)))
def test_yx(self): yx = QubitOperator(((0, 'Y'), (1, 'X')), -0.5) transmed_yx = reverse_jordan_wigner(yx) retransmed_yx = jordan_wigner(transmed_yx) expected1 = 1j * (FermionOperator(((0, 1), )) + FermionOperator( ((0, 0), ))) expected2 = -0.5 * (FermionOperator(((1, 1), )) + FermionOperator( ((1, 0), ))) expected = expected1 * expected2 self.assertTrue(yx.isclose(retransmed_yx)) self.assertTrue( normal_ordered(transmed_yx).isclose(normal_ordered(expected)))
def ordered_low_depth_terms_no_info(hamiltonian): """Give terms from Hamiltonian in dictionary output order. Args: hamiltonian (FermionOperator): The Hamiltonian. Returns: A list of terms from the Hamiltonian in simulated order. Notes: Assumes the Hamiltonian is in the form discussed in Kivlichan et al., "Quantum Simulation of Electronic Structure with Linear Depth and Connectivity", arxiv:1711.04789. This constrains the Hamiltonian to have only hopping terms (i^ j + j^ i) and potential terms which are products of at most two number operators (n_i or n_i n_j). """ n_qubits = count_qubits(hamiltonian) hamiltonian = normal_ordered(hamiltonian) terms = [] for operators, coefficient in iteritems(hamiltonian.terms): terms += [FermionOperator(operators, coefficient)] return terms
def dual_basis_jellium_hamiltonian(grid_length, dimension=3, wigner_seitz_radius=10., n_particles=None, spinless=True): """Return the jellium Hamiltonian with the given parameters. Args: grid_length (int): The number of spatial orbitals per dimension. dimension (int): The dimension of the system. wigner_seitz_radius (float): The radius per particle in Bohr. n_particles (int): The number of particles in the system. Defaults to half filling if not specified. """ n_qubits = grid_length**dimension if not spinless: n_qubits *= 2 if n_particles is None: # Default to half filling fraction. n_particles = n_qubits // 2 if not (0 <= n_particles <= n_qubits): raise ValueError('n_particles must be between 0 and the number of' ' spin-orbitals.') # Compute appropriate length scale. length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles, dimension) grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless=spinless, plane_wave=False) hamiltonian = normal_ordered(hamiltonian) hamiltonian.compress() return hamiltonian
def setUp(self): geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered( get_fermion_operator(self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian)
def simulation_ordered_grouped_low_depth_terms_with_info( hamiltonian, input_ordering=None): """Give terms from the dual basis Hamiltonian in simulated order. Uses the simulation ordering, grouping terms into hopping (i^ j + j^ i) and number (i^j^ i j + c_i i^ i + c_j j^ j) operators. Pre-computes term information (indices each operator acts on, as well as whether each operator is a hopping operator. Args: hamiltonian (FermionOperator): The Hamiltonian. input_ordering (list): The initial Jordan-Wigner canonical order. Returns: A 3-tuple of terms from the Hamiltonian in order of simulation, the indices they act on, and whether they are hopping operators (both also in the same order). Notes: Follows the "stagger"-based simulation order discussed in Kivlichan et al., "Quantum Simulation of Electronic Structure with Linear Depth and Connectivity", arxiv:1711.04789; as such, the only permitted types of terms are hopping (i^ j + j^ i) and potential terms which are products of at most two number operators. """ n_qubits = count_qubits(hamiltonian) hamiltonian = normal_ordered(hamiltonian) ordered_terms = [] ordered_indices = [] ordered_is_hopping_operator = [] # If no input mode ordering is specified, default to range(n_qubits). try: input_ordering = list(input_ordering) except TypeError: input_ordering = list(range(n_qubits)) # Half a second-order Trotter step reverses the input ordering: this tells # us how much we need to include in the ordered list of terms. final_ordering = list(reversed(input_ordering)) # Follow odd-even transposition sort. In alternating steps, swap each even # qubits with the odd qubit to its right, and in the next step swap each # the odd qubits with the even qubit to its right. Do this until the input # ordering has been reversed. parity = 0 while input_ordering != final_ordering: results = stagger_with_info(hamiltonian, input_ordering, parity) terms_in_step, indices_in_step, is_hopping_operator_in_step = results ordered_terms.extend(terms_in_step) ordered_indices.extend(indices_in_step) ordered_is_hopping_operator.extend(is_hopping_operator_in_step) # Alternate even and odd steps of the reversal procedure. parity = 1 - parity return (ordered_terms, ordered_indices, ordered_is_hopping_operator)
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 double_commutator(op1, op2, op3, indices2=None, indices3=None, is_hopping_operator2=None, is_hopping_operator3=None): """Return the double commutator [op1, [op2, op3]]. Assumes the operators are from the dual basis Hamiltonian. Args: op1, op2, op3 (FermionOperators): operators for the commutator. 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_8mode_ffft_with_external_swaps_on_single_logical_state(self): n_qubits = 8 grid = Grid(dimensions=1, length=n_qubits, scale=1.0) eng = MainEngine() register = eng.allocate_qureg(n_qubits) state_index = 157 prepare_logical_state(register, state_index) ffft(eng, register, n_qubits) Ph(3 * numpy.pi / 4) | register[0] eng.flush() wvfn = ordered_wavefunction(eng) fermion_operator = prepare_integer_fermion_operator(state_index) # Swap 01234567 to 45670123 for fourier_transform. Additionally, # the FFFT's ordering is 04261537, so swap 04261537 to 01234567, # and then 01234567 to 45670123. swap_mode_list = ([1, 3, 5, 2, 4, 1, 3, 5] + [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3]) for mode in swap_mode_list: fermion_operator = normal_ordered(fermion_operator) fermion_operator = swap_adjacent_fermionic_modes( fermion_operator, mode) ffft_result = fourier_transform(fermion_operator, grid, spinless=True) ffft_result = normal_ordered(ffft_result) # After the FFFT, swap 45670123 -> 01234567. swap_mode_list = [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3] for mode in swap_mode_list: ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode) converted_wvfn = numpy.zeros(2 ** n_qubits, dtype=complex) for term in ffft_result.terms: index = sum(2 ** site[0] for site in term) converted_wvfn[index] = ffft_result.terms[term] All(Measure) | register self.assertTrue(numpy.allclose(wvfn, converted_wvfn))
def test_sum_of_ordered_terms_equals_full_hamiltonian_odd_side_len(self): hamiltonian = normal_ordered( fermi_hubbard(5, 5, 1.0, -0.3, periodic=False)) hamiltonian.compress() terms = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian)[0] terms_total = sum(terms, FermionOperator.zero()) self.assertTrue(terms_total.isclose(hamiltonian))
def test_4mode_ffft_with_external_swaps_all_logical_states(self): n_qubits = 4 grid = Grid(dimensions=1, length=n_qubits, scale=1.0) for i in range(2**n_qubits): eng = MainEngine() register = eng.allocate_qureg(n_qubits) prepare_logical_state(register, i) ffft(eng, register, n_qubits) Ph(3 * numpy.pi / 4) | register[0] eng.flush() wvfn = ordered_wavefunction(eng) fermion_operator = prepare_integer_fermion_operator(i) # Reorder the modes for correct input to the FFFT. # Swap 0123 to 2301 for fourier_transform. Additionally, the # FFFT's ordering is 0213, so connect 0213 -> 0123 -> 2301. swap_mode_list = [1] + [1, 0, 2, 1] for mode in swap_mode_list: fermion_operator = normal_ordered(fermion_operator) fermion_operator = swap_adjacent_fermionic_modes( fermion_operator, mode) ffft_result = fourier_transform(fermion_operator, grid, spinless=True) ffft_result = normal_ordered(ffft_result) swap_mode_list = [1, 0, 2, 1] # After FFFT, swap 2301 -> 0123 for mode in swap_mode_list: ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode) converted_wvfn = numpy.zeros(2**n_qubits, dtype=complex) for term in ffft_result.terms: index = sum(2**site[0] for site in term) converted_wvfn[index] = ffft_result.terms[term] All(Measure) | register self.assertTrue(numpy.allclose(wvfn, converted_wvfn))
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_total_length_odd_side_length_full_hubbard(self): hamiltonian = normal_ordered( fermi_hubbard(5, 5, -1., -0.3, periodic=False)) hamiltonian.compress() # Unpack result into terms, indices they act on, and whether they're # hopping operators. result = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian) terms, indices, is_hopping = result self.assertEqual(len(terms), 105)
def test_majorana_form(self): """Test getting the Majorana form.""" majorana_matrix, majorana_constant = self.quad_ham_npc.majorana_form() # Convert the Majorana form to a FermionOperator majorana_op = FermionOperator((), majorana_constant) for i in range(2 * self.n_qubits): if i < self.n_qubits: left_op = majorana_operator((i, 1)) else: left_op = majorana_operator((i - self.n_qubits, 0)) for j in range(2 * self.n_qubits): if j < self.n_qubits: right_op = majorana_operator((j, 1), majorana_matrix[i, j]) else: right_op = majorana_operator((j - self.n_qubits, 0), majorana_matrix[i, j]) majorana_op += .5j * left_op * right_op # Get FermionOperator for original Hamiltonian fermion_operator = normal_ordered( get_fermion_operator(self.quad_ham_npc)) self.assertTrue(normal_ordered(majorana_op).isclose(fermion_operator))
def test_uccsd_singlet_symmetries(self): """Test that the singlet generator has the correct symmetries.""" test_orbitals = 8 test_electrons = 4 packed_amplitude_size = uccsd_singlet_paramsize( test_orbitals, test_electrons) packed_amplitudes = randn(int(packed_amplitude_size)) generator = uccsd_singlet_generator(packed_amplitudes, test_orbitals, test_electrons) # Construct symmetry operators sz = sz_operator(test_orbitals) s_squared = s_squared_operator(test_orbitals) # Check the symmetries comm_sz = normal_ordered(commutator(generator, sz)) comm_s_squared = normal_ordered(commutator(generator, s_squared)) zero = FermionOperator() self.assertEqual(comm_sz, zero) self.assertEqual(comm_s_squared, zero)
def get_qubit_expectations(self, qubit_operator): """Return expectations of QubitOperator in new QubitOperator. Args: qubit_operator: QubitOperator instance to be evaluated on this InteractionRDM. Returns: QubitOperator: QubitOperator with coefficients corresponding to expectation values of those operators. Raises: InteractionRDMError: Observable not contained in 1-RDM or 2-RDM. """ from openfermion.transforms import reverse_jordan_wigner qubit_operator_expectations = copy.deepcopy(qubit_operator) for qubit_term in qubit_operator_expectations.terms: expectation = 0. # Map qubits back to fermions. reversed_fermion_operators = reverse_jordan_wigner( QubitOperator(qubit_term)) reversed_fermion_operators = normal_ordered( reversed_fermion_operators) # Loop through fermion terms. for fermion_term in reversed_fermion_operators.terms: coefficient = reversed_fermion_operators.terms[fermion_term] # Handle molecular term. if FermionOperator(fermion_term).is_molecular_term(): if not fermion_term: expectation += coefficient else: indices = [operator[0] for operator in fermion_term] if len(indices) == 2: # One-body term indices = tuple(zip(indices, (1, 0))) else: # Two-body term indices = tuple(zip(indices, (1, 1, 0, 0))) rdm_element = self[indices] expectation += rdm_element * coefficient # Handle non-molecular terms. elif len(fermion_term) > 4: raise InteractionRDMError('Observable not contained ' 'in 1-RDM or 2-RDM.') qubit_operator_expectations.terms[qubit_term] = expectation return qubit_operator_expectations
def test_sum_of_ordered_terms_equals_full_side_length_2_hopping_only(self): hamiltonian = normal_ordered( fermi_hubbard(2, 2, 1., 0.0, periodic=False)) hamiltonian.compress() # Unpack result into terms, indices they act on, and whether they're # hopping operators. result = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian) terms, indices, is_hopping = result terms_total = sum(terms, FermionOperator.zero()) self.assertTrue(terms_total.isclose(hamiltonian))
def test_error_bound_using_info_even_side_length(self): # Generate the Hamiltonian. hamiltonian = normal_ordered( fermi_hubbard(4, 4, 0.5, 0.2, periodic=False)) hamiltonian.compress() # Unpack result into terms, indices they act on, and whether they're # hopping operators. result = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian) terms, indices, is_hopping = result self.assertAlmostEqual( low_depth_second_order_trotter_error_bound(terms, indices, is_hopping), 13.59)
def test_is_hopping_operator_terms_with_info(self): hamiltonian = normal_ordered( fermi_hubbard(5, 5, 1., -1., periodic=False)) hamiltonian.compress() # Unpack result into terms, indices they act on, and whether they're # hopping operators. result = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian) terms, indices, is_hopping = result for i in range(len(terms)): single_term = list(terms[i].terms)[0] is_hopping_term = not (single_term[1][1] or single_term[0][0] == single_term[1][0]) self.assertEqual(is_hopping_term, is_hopping[i])
def test_correct_indices_terms_with_info(self): hamiltonian = normal_ordered( fermi_hubbard(5, 5, 1., -1., periodic=False)) hamiltonian.compress() # Unpack result into terms, indices they act on, and whether they're # hopping operators. result = simulation_ordered_grouped_hubbard_terms_with_info( hamiltonian) terms, indices, is_hopping = result for i in range(len(terms)): term = list(terms[i].terms) term_indices = set() for single_term in term: term_indices = term_indices.union( [single_term[j][0] for j in range(len(single_term))]) self.assertEqual(term_indices, indices[i])
def setUp(self): grid_length = 4 dimension = 1 wigner_seitz_radius = 10. self.spinless = True self.n_spatial_orbitals = grid_length**dimension n_qubits = self.n_spatial_orbitals self.n_particles = 3 # Compute appropriate length scale and the corresponding grid. length_scale = wigner_seitz_length_scale(wigner_seitz_radius, self.n_particles, dimension) self.grid1 = Grid(dimension, grid_length, length_scale) # Get the occupied orbitals of the plane-wave basis Hartree-Fock state. hamiltonian = jellium_model(self.grid1, self.spinless, plane_wave=True) hamiltonian = normal_ordered(hamiltonian) hamiltonian.compress() occupied_states = numpy.array( lowest_single_particle_energy_states(hamiltonian, self.n_particles)) self.hf_state_index1 = numpy.sum(2**occupied_states) self.hf_state1 = csc_matrix(([1.0], ([self.hf_state_index1], [0])), shape=(2**n_qubits, 1)) self.orbital_occupations1 = [ digit == '1' for digit in bin(self.hf_state_index1)[2:] ][::-1] self.occupied_orbitals1 = [ index for index, occupied in enumerate(self.orbital_occupations1) if occupied ] self.reversed_occupied_orbitals1 = list(self.occupied_orbitals1) for i in range(len(self.reversed_occupied_orbitals1)): self.reversed_occupied_orbitals1[i] = -1 + int( numpy.log2(self.hf_state1.shape[0]) ) - self.reversed_occupied_orbitals1[i] self.reversed_hf_state_index1 = sum( 2**index for index in self.reversed_occupied_orbitals1)
def dual_basis_hamiltonian(n_dimensions, system_size, plane_wave=False, spinless=True): """Return the plane wave dual basis hamiltonian with the given parameters. Returns: the Hamiltonian as a normal-ordered FermionOperator. Args: n_dimensions (int): The number of dimensions in the system. system_size (int): The side length along each dimension. plane_wave (bool): Whether the system is in the plane wave basis. spinless (bool): Whether the system is spinless. Notes: Follows Eq. 10 of https://arxiv.org/pdf/1706.00023.pdf. """ grid = Grid(n_dimensions, length=system_size, scale=1.0 * system_size) hamiltonian = plane_wave_hamiltonian(grid, spinless=spinless, plane_wave=plane_wave) return normal_ordered(hamiltonian)
def standardized_dual_basis_jellium_hamiltonian(grid_length, dimension, wigner_seitz_radius=10., n_particles=None, spinless=True): """Return the jellium Hamiltonian in the dual basis in standardized form (normal ordered, compressed, and without constant offset) with the given parameters. Args: grid_length (int): The number of spatial orbitals along each dimension. dimension (int): The number of spatial dimensions in the system. wigner_seitz_radius (float): The Wigner-Seitz radius per particle, in Bohr. Defaults to 10. n_particles (int): The number of particles in the system. Defaults to half filling, rounding down, if not specified. spinless (boolean): Whether to generate the Hamiltonian without or with spin. Defaults to True. """ n_qubits = grid_length**dimension if not spinless: n_qubits *= 2 if n_particles is None: # Default to half filling fraction. n_particles = n_qubits // 2 if not (0 <= n_particles <= n_qubits): raise ValueError('n_particles must be between 0 and the number of' ' spin-orbitals.') # Compute appropriate length scale. length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles, dimension) grid = Grid(dimension, grid_length, length_scale) hamiltonian = normal_ordered( jellium_model(grid, spinless=spinless, plane_wave=False)) hamiltonian.compress() return hamiltonian
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)
def test_1d5_with_spin_7particles(self): dimension = 1 grid_length = 5 n_spatial_orbitals = grid_length**dimension wigner_seitz_radius = 9.3 spinless = False n_qubits = n_spatial_orbitals if not spinless: n_qubits *= 2 n_particles_big = 7 length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles_big, dimension) self.grid3 = Grid(dimension, grid_length, length_scale) # Get the occupied orbitals of the plane-wave basis Hartree-Fock state. hamiltonian = jellium_model(self.grid3, spinless, plane_wave=True) hamiltonian = normal_ordered(hamiltonian) hamiltonian.compress() occupied_states = numpy.array( lowest_single_particle_energy_states(hamiltonian, n_particles_big)) self.hf_state_index3 = numpy.sum(2**occupied_states) self.hf_state3 = csc_matrix(([1.0], ([self.hf_state_index3], [0])), shape=(2**n_qubits, 1)) self.orbital_occupations3 = [ digit == '1' for digit in bin(self.hf_state_index3)[2:] ][::-1] self.occupied_orbitals3 = [ index for index, occupied in enumerate(self.orbital_occupations3) if occupied ] self.reversed_occupied_orbitals3 = list(self.occupied_orbitals3) for i in range(len(self.reversed_occupied_orbitals3)): self.reversed_occupied_orbitals3[i] = -1 + int( numpy.log2(self.hf_state3.shape[0]) ) - self.reversed_occupied_orbitals3[i] self.reversed_hf_state_index3 = sum( 2**index for index in self.reversed_occupied_orbitals3) operator = (FermionOperator('6^ 0^ 1^ 3 5 4', 2) + FermionOperator('7^ 2^ 4 1') + FermionOperator('3^ 3', 2.1) + FermionOperator('5^ 3^ 1 0', 7.3)) operator = normal_ordered(operator) transformed_operator = normal_ordered( fourier_transform(operator, self.grid3, spinless)) expected = 1.66 - 0.0615536707435j # Calculated with expected = expectation(get_sparse_operator( # transformed_operator), self.hf_state3) actual = expectation_db_operator_with_pw_basis_state( operator, self.reversed_occupied_orbitals3, n_spatial_orbitals, self.grid3, spinless) self.assertAlmostEqual(expected, actual)