def test_jordan_wigner_dual_basis_hamiltonian_bad_element(self): grid = Grid(dimensions=3, scale=1.0, length=4) with self.assertRaises(ValueError): jordan_wigner_dual_basis_hamiltonian(grid, geometry=[('Unobtainium', (0, 0, 0))])
def test_plane_wave_hamiltonian_default_to_jellium_with_no_geometry(self): grid = Grid(dimensions=1, scale=1.0, length=4) self.assertTrue(plane_wave_hamiltonian(grid) == jellium_model(grid))
def test_equality(self): eq = EqualsTester(self) eq.make_equality_pair(lambda: Grid(dimensions=5, length=3, scale=0.5)) eq.add_equality_group(Grid(dimensions=4, length=3, scale=0.5)) eq.add_equality_group(Grid(dimensions=5, length=4, scale=0.5)) eq.add_equality_group(Grid(dimensions=5, length=3, scale=0.25))
def test_momentum_vector(self): grid = Grid(dimensions=1, length=3, scale=2. * numpy.pi) test_output = [grid.momentum_vector(i) for i in range(grid.length[0])] correct_output = [-1., 0, 1.] self.assertEqual(correct_output, test_output) grid = Grid(dimensions=1, length=2, scale=2. * numpy.pi) test_output = [grid.momentum_vector(i) for i in range(grid.length[0])] correct_output = [-1., 0.] self.assertEqual(correct_output, test_output) grid = Grid(dimensions=1, length=11, scale=2. * numpy.pi) for i in range(grid.length[0]): self.assertAlmostEqual( -grid.momentum_vector(i), grid.momentum_vector(grid.length[0] - i - 1)) # Test in 2D. grid = Grid(dimensions=2, length=3, scale=2. * numpy.pi) test_input = [] test_output = [] for i in range(3): for j in range(3): test_input += [(i, j)] test_output += [grid.momentum_vector((i, j))] correct_output = numpy.array([[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1]]) self.assertAlmostEqual(0., numpy.amax(test_output - correct_output))
def test_initialize(self): g1 = Grid(dimensions=3, length=3, scale=1.0) scale_matrix = numpy.diag([1.0] * 3) g2 = Grid(dimensions=3, length=(3, 3, 3), scale=scale_matrix) self.assertEqual(g1, g2)
def test_no_errors_in_call(self): # No exception _ = Grid(dimensions=1, length=1, scale=1.0) _ = Grid(dimensions=2, length=3, scale=0.01) _ = Grid(dimensions=23, length=34, scale=45.0)
def test_plane_wave_hamiltonian_bad_element(self): grid = Grid(dimensions=3, scale=1.0, length=4) with self.assertRaises(ValueError): plane_wave_hamiltonian(grid, geometry=[('Unobtainium', (0, 0, 0))])
def test_jordan_wigner_dual_basis_hamiltonian_default_to_jellium(self): grid = Grid(dimensions=1, scale=1.0, length=4) self.assertTrue(jordan_wigner_dual_basis_hamiltonian(grid) == jordan_wigner(jellium_model(grid, plane_wave=False)))
def test_coefficients(self): # Test that the coefficients post-JW transform are as claimed in paper. grid = Grid(dimensions=2, length=3, scale=2.) spinless = 1 n_orbitals = grid.num_points n_qubits = (2**(1 - spinless)) * n_orbitals volume = grid.volume_scale() # Kinetic operator. kinetic = dual_basis_kinetic(grid, spinless) qubit_kinetic = jordan_wigner(kinetic) # Potential operator. potential = dual_basis_potential(grid, spinless) qubit_potential = jordan_wigner(potential) # Check identity. identity = tuple() kinetic_coefficient = qubit_kinetic.terms[identity] potential_coefficient = qubit_potential.terms[identity] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = grid.momentum_vector(indices) paper_kinetic_coefficient += float(n_qubits) * momenta.dot( momenta) / float(4. * n_orbitals) if momenta.any(): potential_contribution = -numpy.pi * float(n_qubits) / float( 2. * momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp. for p in range(n_qubits): zp = ((p, 'Z'), ) kinetic_coefficient = qubit_kinetic.terms[zp] potential_coefficient = qubit_potential.terms[zp] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = grid.momentum_vector(indices) paper_kinetic_coefficient -= momenta.dot(momenta) / float( 4. * n_orbitals) if momenta.any(): potential_contribution = numpy.pi / float( momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp Zq. if spinless: spins = [None] for indices_a in grid.all_points_indices(): for indices_b in grid.all_points_indices(): potential_coefficient = 0. paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. position_a = grid.position_vector(indices_a) position_b = grid.position_vector(indices_b) differences = position_b - position_a for spin_a in spins: for spin_b in spins: p = grid.orbital_id(indices_a, spin_a) q = grid.orbital_id(indices_b, spin_b) if p == q: continue zpzq = ((min(p, q), 'Z'), (max(p, q), 'Z')) if zpzq in qubit_potential.terms: potential_coefficient = qubit_potential.terms[zpzq] for indices_c in grid.all_points_indices(): momenta = grid.momentum_vector(indices_c) if momenta.any(): potential_contribution = numpy.pi * numpy.cos( differences.dot(momenta)) / float( momenta.dot(momenta) * volume) paper_potential_coefficient += ( potential_contribution) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient)
def __init__(self, a=7.72, n=3, n_active_el=2, n_active_orb=4): self.a = a self.n = n self.n_active_el = n_active_el self.n_active_orb = n_active_orb self.N_units = 4 self.opt_energy = None self.opt_amplitudes = None species_a = 'H' species_b = 'Li' # Construct a fermion Hamiltonian grid = Grid(3, self.n, self.a) geometry = [(species_a, (0, 0, 0)), (species_a, (0, 0.5, 0.5)), (species_a, (0.5, 0, 0.5)), (species_a, (0.5, 0.5, 0)), (species_b, (0.5, 0.5, 0.5)), (species_b, (0.5, 0, 0)), (species_b, (0, 0.5, 0)), (species_b, (0, 0, 0.5))] self.fermion_hamiltonian = plane_wave_hamiltonian( grid, geometry=geometry, spinless=False, plane_wave=False, include_constant=False, e_cutoff=None) # Freeze specified orbitals n_qubits = 2 * n * n * n n_electrons = 4 * 3 + 4 * 1 # Determine the indices of occupied orbitals to be frozen if self.n == 3: to_fill = ((1, 1, 1), (0, 1, 1), (2, 1, 1), (1, 0, 1), (1, 2, 1), (1, 1, 0), (1, 1, 2), (0, 0, 1)) else: to_fill = range(n_electrons - self.n_active_el) to_fill_ids = [] for s in to_fill: to_fill_ids.append(orbital_id(grid, s, 0)) to_fill_ids.append(orbital_id(grid, s, 1)) to_fill_ids = to_fill_ids[0:(n_electrons - self.n_active_el)] #print(to_fill_ids) #print('# of terms: {}'.format(len(self.fermion_hamiltonian.terms))) self.fermion_hamiltonian = freeze_orbitals(self.fermion_hamiltonian, to_fill_ids) #print('# of terms: {}'.format(len(self.fermion_hamiltonian.terms))) # Freeze unoccupied orbitals to_freeze_ids = range(self.n_active_orb, n_qubits - (n_electrons - self.n_active_el)) #print(to_freeze_ids) self.fermion_hamiltonian = freeze_orbitals(self.fermion_hamiltonian, [], to_freeze_ids) print('# of terms: {}'.format(len(self.fermion_hamiltonian.terms))) # Construct qubit Hamiltonian self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Initialize com;iler engine self.compiler_engine = uccsd_trotter_engine()
def test_8mode_ffft_with_external_swaps_equal_expectation_values(self): n_qubits = 8 grid = Grid(dimensions=1, length=n_qubits, scale=1.0) dual_basis = jellium_model(grid, spinless=True, plane_wave=False) ffft_result = normal_ordered(dual_basis) # 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: ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode) ffft_result = normal_ordered(ffft_result) ffft_result = fourier_transform(ffft_result, 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) ffft_result = normal_ordered(ffft_result) jw_dual_basis = jordan_wigner(dual_basis) jw_plane_wave = jordan_wigner(ffft_result) # Do plane wave and dual basis calculations simultaneously. pw_engine = MainEngine() pw_wavefunction = pw_engine.allocate_qureg(n_qubits) db_engine = MainEngine() db_wavefunction = db_engine.allocate_qureg(n_qubits) pw_engine.flush() db_engine.flush() # Choose random state. state = numpy.zeros(2**n_qubits, dtype=complex) for i in range(len(state)): state[i] = (random.random() * numpy.exp(1j * 2 * numpy.pi * random.random())) state /= numpy.linalg.norm(state) # Put randomly chosen state in the registers. pw_engine.backend.set_wavefunction(state, pw_wavefunction) db_engine.backend.set_wavefunction(state, db_wavefunction) prepare_logical_state(pw_wavefunction, i) prepare_logical_state(db_wavefunction, i) All(H) | [pw_wavefunction[1], pw_wavefunction[3]] All(H) | [db_wavefunction[1], db_wavefunction[3]] ffft(db_engine, db_wavefunction, n_qubits) Ph(3 * numpy.pi / 4) | db_wavefunction[0] # Flush the engine and compute expectation values and eigenvalues. pw_engine.flush() db_engine.flush() plane_wave_expectation_value = ( pw_engine.backend.get_expectation_value(jw_dual_basis, pw_wavefunction)) dual_basis_expectation_value = ( db_engine.backend.get_expectation_value(jw_plane_wave, db_wavefunction)) All(Measure) | pw_wavefunction All(Measure) | db_wavefunction self.assertAlmostEqual(plane_wave_expectation_value, dual_basis_expectation_value)
def test_plane_wave_period_cutoff(self): # TODO: After figuring out the correct formula for period cutoff for # dual basis, change period_cutoff to default, and change # hamiltonian_1 to a real jellium_model for real integration test. spinless = True # [[spatial dimension, fieldline dimension]] dims = [[2, 2], [2, 3], [3, 3]] for dim in dims: # If dim[0] == 2, get range(2, 4). # If dim[0] == 3, get range(2, 3). for length in range(2, 6 - dim[0]): grid = Grid(dimensions=dim[0], length=length, scale=1.0) period_cutoff = grid.volume_scale()**(1. / grid.dimensions) hamiltonian_1 = jellium_model(grid, spinless=spinless, plane_wave=True, include_constant=False, fieldlines=dim[1]) jw_1 = jordan_wigner(hamiltonian_1) spectrum_1 = eigenspectrum(jw_1) hamiltonian_2 = jellium_model(grid, spinless=spinless, plane_wave=True, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) jw_2 = jordan_wigner(hamiltonian_2) spectrum_2 = eigenspectrum(jw_2) max_diff = numpy.amax(numpy.absolute(spectrum_1 - spectrum_2)) # Checks if non-periodic and periodic cases are different. self.assertGreater(max_diff, 0.) # TODO: This is only for code coverage. Remove after having real # integration test. momentum_hamiltonian = jellium_model( grid, spinless=spinless, plane_wave=True, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) position_hamiltonian = jellium_model( grid, spinless=spinless, plane_wave=False, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) # Confirm they are Hermitian momentum_hamiltonian_operator = ( get_sparse_operator(momentum_hamiltonian)) self.assertTrue(is_hermitian(momentum_hamiltonian_operator)) position_hamiltonian_operator = ( get_sparse_operator(position_hamiltonian)) self.assertTrue(is_hermitian(position_hamiltonian_operator)) # Diagonalize. jw_momentum = jordan_wigner(momentum_hamiltonian) jw_position = jordan_wigner(position_hamiltonian) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm spectra are the same. difference = numpy.amax( numpy.absolute(momentum_spectrum - position_spectrum)) self.assertAlmostEqual(difference, 0.)
def test_coefficients(self): # Test that the coefficients post-JW transform are as claimed in paper. spinless = True non_periodics = [False, True] # [[spatial dimension, fieldline dimension]]. dims = [[2, 2], [2, 3], [3, 3]] # Reference length scale where the 2D Coulomb potential is zero. R0 = 1e8 for dim in dims: # If dim[0] == 2, get range(2, 4). # If dim[0] == 3, get range(2, 3). for length in range(2, 6 - dim[0]): for non_periodic in non_periodics: grid = Grid(dimensions=dim[0], length=length, scale=2.) n_orbitals = grid.num_points n_qubits = (2**(1 - spinless)) * n_orbitals volume = grid.volume_scale() period_cutoff = None if non_periodic: period_cutoff = volume**(1. / grid.dimensions) fieldlines = dim[1] # Kinetic operator. kinetic = dual_basis_kinetic(grid, spinless) qubit_kinetic = jordan_wigner(kinetic) # Potential operator. potential = dual_basis_potential( grid, spinless, non_periodic=non_periodic, period_cutoff=period_cutoff, fieldlines=fieldlines) qubit_potential = jordan_wigner(potential) # Check identity. identity = tuple() kinetic_coefficient = qubit_kinetic.terms[identity] potential_coefficient = qubit_potential.terms[identity] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = grid.momentum_vector(indices) momenta_squared = momenta.dot(momenta) paper_kinetic_coefficient += (float(n_qubits) * momenta_squared / float(4. * n_orbitals)) if momenta.any(): potential_contribution = 0. # 3D case. if grid.dimensions == 3: potential_contribution = -( numpy.pi * float(n_qubits) / float(2. * momenta_squared * volume)) # If non-periodic. if non_periodic: correction = 1.0 - numpy.cos( period_cutoff * numpy.sqrt(momenta_squared)) potential_contribution *= correction # 2D case. elif grid.dimensions == 2: V_nu = 0. # 2D Coulomb potential. if fieldlines == 2: # If non-periodic. if non_periodic: Dkv = period_cutoff * numpy.sqrt( momenta_squared) V_nu = ( 2. * numpy.pi / momenta_squared * (Dkv * numpy.log(R0 / period_cutoff) * scipy.special.jv(1, Dkv) - scipy.special.jv(0, Dkv))) # If periodic. else: var1 = 4. / momenta_squared var2 = 0.25 * momenta_squared V_nu = 0.5 * numpy.complex128( mpmath.meijerg([[1., 1.5, 2.], []], [[1.5], []], var1) - mpmath.meijerg( [[-0.5, 0., 0.], []], [[-0.5, 0.], [-1.]], var2)) # 3D Coulomb potential. elif fieldlines == 3: # If non-periodic. if non_periodic: var = (-0.25 * period_cutoff**2 * momenta_squared) V_nu = numpy.complex128( 2 * numpy.pi * period_cutoff * mpmath.hyp1f2(0.5, 1., 1.5, var)) # If periodic. else: V_nu = (2 * numpy.pi / numpy.sqrt(momenta_squared)) potential_contribution = (-float(n_qubits) / (8. * volume) * V_nu) paper_potential_coefficient += potential_contribution # Check if coefficients are equal. self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp. for p in range(n_qubits): zp = ((p, 'Z'), ) kinetic_coefficient = qubit_kinetic.terms[zp] potential_coefficient = qubit_potential.terms[zp] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = grid.momentum_vector(indices) momenta_squared = momenta.dot(momenta) paper_kinetic_coefficient -= ( momenta_squared / float(4. * n_orbitals)) if momenta.any(): potential_contribution = 0. # 3D case. if grid.dimensions == 3: potential_contribution = ( numpy.pi / float(momenta_squared * volume)) # If non-periodic. if non_periodic: correction = 1.0 - numpy.cos( period_cutoff * numpy.sqrt(momenta_squared)) potential_contribution *= correction # 2D case. elif grid.dimensions == 2: V_nu = 0. # 2D Coulomb potential. if fieldlines == 2: # If non-periodic. if non_periodic: Dkv = period_cutoff * numpy.sqrt( momenta_squared) V_nu = (2. * numpy.pi / momenta_squared * (Dkv * numpy.log( R0 / period_cutoff) * scipy.special.jv(1, Dkv) - scipy.special.jv(0, Dkv))) # If periodic. else: var1 = 4. / momenta_squared var2 = 0.25 * momenta_squared V_nu = 0.5 * numpy.complex128( mpmath.meijerg( [[1., 1.5, 2.], []], [[1.5], []], var1) - mpmath.meijerg( [[-0.5, 0., 0.], []], [[-0.5, 0.], [-1.]], var2)) # 3D Coulomb potential. elif fieldlines == 3: # If non-periodic. if non_periodic: var = (-0.25 * period_cutoff**2 * momenta_squared) V_nu = numpy.complex128( 2 * numpy.pi * period_cutoff * mpmath.hyp1f2( 0.5, 1., 1.5, var)) # If periodic. else: V_nu = ( 2 * numpy.pi / numpy.sqrt(momenta_squared)) potential_contribution = (1. / (4. * volume) * V_nu) paper_potential_coefficient += potential_contribution # Check if coefficients are equal. self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp Zq. if spinless: spins = [None] for indices_a in grid.all_points_indices(): for indices_b in grid.all_points_indices(): potential_coefficient = 0. paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. position_a = grid.position_vector(indices_a) position_b = grid.position_vector(indices_b) differences = position_b - position_a for spin_a in spins: for spin_b in spins: p = grid.orbital_id(indices_a, spin_a) q = grid.orbital_id(indices_b, spin_b) if p == q: continue zpzq = ((min(p, q), 'Z'), (max(p, q), 'Z')) if zpzq in qubit_potential.terms: potential_coefficient = qubit_potential.terms[ zpzq] for indices_c in grid.all_points_indices(): momenta = grid.momentum_vector( indices_c) momenta_squared = momenta.dot(momenta) if momenta.any(): potential_contribution = 0. # 3D case. if grid.dimensions == 3: potential_contribution = ( numpy.pi * numpy.cos( differences.dot( momenta)) / float(momenta_squared * volume)) # If non-periodic. if non_periodic: correction = 1.0 - numpy.cos( period_cutoff * numpy. sqrt(momenta_squared)) potential_contribution *= correction # 2D case. elif grid.dimensions == 2: V_nu = 0. # 2D Coulomb potential. if fieldlines == 2: # If non-periodic. if non_periodic: Dkv = ( period_cutoff * numpy.sqrt( momenta_squared )) V_nu = ( 2. * numpy.pi / momenta_squared * (Dkv * numpy.log( R0 / period_cutoff) * scipy.special. jv(1, Dkv) - scipy.special.jv( 0, Dkv))) # If periodic. else: var1 = 4. / momenta_squared var2 = 0.25 * momenta_squared V_nu = 0.5 * numpy.complex128( mpmath.meijerg([[ 1., 1.5, 2. ], []], [[1.5], [] ], var1) - mpmath.meijerg([[ -0.5, 0., 0. ], []], [[ -0.5, 0. ], [-1.]], var2)) # 3D Coulomb potential. elif fieldlines == 3: # If non-periodic. if non_periodic: var = -0.25 * period_cutoff**2 * momenta_squared V_nu = numpy.complex128( 2 * numpy.pi * period_cutoff * mpmath.hyp1f2( 0.5, 1., 1.5, var)) # If periodic. else: V_nu = 2 * numpy.pi / numpy.sqrt( momenta_squared) potential_contribution = ( numpy.cos( differences.dot( momenta)) / (4. * volume) * V_nu) paper_potential_coefficient += potential_contribution # Check if coefficients are equal. self.assertAlmostEqual( potential_coefficient, paper_potential_coefficient)