def plane_wave_u_operator(n_dimensions, grid_length, length_scale, nuclear_charges, spinless): """Return the external potential operator in plane wave basis. Args: n_dimensions: An int giving the number of dimensions for the model. grid_length: Int, the number of points in one dimension of the grid. length_scale: Float, the real space length of a box dimension. nuclear_charges: 3D int array, the nuclear charges. spinless: Bool, whether to use the spinless model or not. Returns: operator: An instance of the FermionOperator class. """ n_points = grid_length**n_dimensions volume = length_scale**float(n_dimensions) prefactor = -4.0 * numpy.pi / volume operator = None if spinless: spins = [None] else: spins = [0, 1] for grid_indices_p in itertools.product(range(grid_length), repeat=n_dimensions): for grid_indices_q in itertools.product(range(grid_length), repeat=n_dimensions): shift = grid_length // 2 grid_indices_p_q = [ (grid_indices_p[i] - grid_indices_q[i] + shift) % grid_length for i in range(n_dimensions) ] momenta_p_q = momentum_vector(grid_indices_p_q, grid_length, length_scale) momenta_p_q_squared = momenta_p_q.dot(momenta_p_q) if momenta_p_q_squared < EQ_TOLERANCE: continue for grid_indices_j in itertools.product(range(grid_length), repeat=n_dimensions): coordinate_j = position_vector(grid_indices_j, grid_length, length_scale) exp_index = 1.0j * momenta_p_q.dot(coordinate_j) coefficient = prefactor / momenta_p_q_squared * \ nuclear_charges[grid_indices_j] * numpy.exp(exp_index) for spin in spins: orbital_p = orbital_id(grid_length, grid_indices_p, spin) orbital_q = orbital_id(grid_length, grid_indices_q, spin) operators = ((orbital_p, 1), (orbital_q, 0)) if operator is None: operator = FermionOperator(operators, coefficient) else: operator += FermionOperator(operators, coefficient) return operator
def plane_wave_external_potential(grid, geometry, spinless): """Return the external potential operator in plane wave basis. Args: grid (Grid): The discretization to use. geometry: A list of tuples giving the coordinates of each atom. example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))]. Distances in atomic units. Use atomic symbols to specify atoms. spinless: Bool, whether to use the spinless model or not. Returns: FermionOperator: The plane wave operator. """ prefactor = -4.0 * numpy.pi / grid.volume_scale() operator = None if spinless: spins = [None] else: spins = [0, 1] for indices_p in grid.all_points_indices(): for indices_q in grid.all_points_indices(): shift = grid.length // 2 grid_indices_p_q = [ (indices_p[i] - indices_q[i] + shift) % grid.length for i in range(grid.dimensions)] momenta_p_q = momentum_vector(grid_indices_p_q, grid) momenta_p_q_squared = momenta_p_q.dot(momenta_p_q) if momenta_p_q_squared < EQ_TOLERANCE: continue for nuclear_term in geometry: coordinate_j = numpy.array(nuclear_term[1]) exp_index = 1.0j * momenta_p_q.dot(coordinate_j) coefficient = (prefactor / momenta_p_q_squared * periodic_hash_table[nuclear_term[0]] * numpy.exp(exp_index)) for spin in spins: orbital_p = orbital_id(grid, indices_p, spin) orbital_q = orbital_id(grid, indices_q, spin) operators = ((orbital_p, 1), (orbital_q, 0)) if operator is None: operator = FermionOperator(operators, coefficient) else: operator += FermionOperator(operators, coefficient) return operator
def test_orbital_id(self): # Test in 1D with spin. grid = Grid(dimensions=1, length=5, scale=1.0) input_coords = [0, 1, 2, 3, 4] tensor_factors_up = [1, 3, 5, 7, 9] tensor_factors_down = [0, 2, 4, 6, 8] test_output_up = [orbital_id(grid, i, 1) for i in input_coords] test_output_down = [orbital_id(grid, i, 0) for i in input_coords] self.assertEqual(test_output_up, tensor_factors_up) self.assertEqual(test_output_down, tensor_factors_down) with self.assertRaises(OrbitalSpecificationError): orbital_id(grid, 6, 1) # Test in 2D without spin. grid = Grid(dimensions=2, length=3, scale=1.0) input_coords = [(0, 0), (0, 1), (1, 2)] tensor_factors = [0, 3, 7] test_output = [orbital_id(grid, i) for i in input_coords] self.assertEqual(test_output, tensor_factors)
def dual_basis_external_potential(grid, geometry, spinless): """Return the external potential in the dual basis of arXiv:1706.00023. Args: grid (Grid): The discretization to use. geometry: A list of tuples giving the coordinates of each atom. example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))]. Distances in atomic units. Use atomic symbols to specify atoms. spinless (bool): Whether to use the spinless model or not. Returns: FermionOperator: The dual basis operator. """ prefactor = -4.0 * numpy.pi / grid.volume_scale() operator = None if spinless: spins = [None] else: spins = [0, 1] for pos_indices in grid.all_points_indices(): coordinate_p = position_vector(pos_indices, grid) for nuclear_term in geometry: coordinate_j = numpy.array(nuclear_term[1], float) for momenta_indices in grid.all_points_indices(): momenta = momentum_vector(momenta_indices, grid) momenta_squared = momenta.dot(momenta) if momenta_squared < EQ_TOLERANCE: continue exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p) coefficient = (prefactor / momenta_squared * periodic_hash_table[nuclear_term[0]] * numpy.exp(exp_index)) for spin_p in spins: orbital_p = orbital_id(grid, pos_indices, spin_p) operators = ((orbital_p, 1), (orbital_p, 0)) if operator is None: operator = FermionOperator(operators, coefficient) else: operator += FermionOperator(operators, coefficient) return operator
def _fourier_transform_helper(hamiltonian, grid, spinless, phase_factor, vec_func_1, vec_func_2): hamiltonian_t = FermionOperator.zero() normalize_factor = numpy.sqrt(1.0 / float(grid.num_points())) for term in hamiltonian.terms: transformed_term = FermionOperator.identity() for ladder_op_mode, ladder_op_type in term: indices_1 = grid_indices(ladder_op_mode, grid, spinless) vec1 = vec_func_1(indices_1, grid) new_basis = FermionOperator.zero() for indices_2 in grid.all_points_indices(): vec2 = vec_func_2(indices_2, grid) spin = None if spinless else ladder_op_mode % 2 orbital = orbital_id(grid, indices_2, spin) exp_index = phase_factor * 1.0j * numpy.dot(vec1, vec2) if ladder_op_type == 1: exp_index *= -1.0 element = FermionOperator(((orbital, ladder_op_type),), numpy.exp(exp_index)) new_basis += element new_basis *= normalize_factor transformed_term *= new_basis # Coefficient. transformed_term *= hamiltonian.terms[term] hamiltonian_t += transformed_term return hamiltonian_t
def inverse_fourier_transform(hamiltonian, n_dimensions, grid_length, length_scale, spinless): """Apply Fourier tranform to change hamiltonian in plane wave dual basis. .. math:: a^\dagger_v = \sqrt{1/N} \sum_m {c^\dagger_m \exp(i k_v r_m)} a_v = \sqrt{1/N} \sum_m {c_m \exp(-i k_v r_m)} Args: hamiltonian: The hamiltonian in plane wave dual basis. n_dimensions: An int giving the number of dimensions for the model. grid_length: Int, the number of points in one dimension of the grid. length_scale: Float, the real space length of a box dimension. spinless: Bool, whether to use the spinless model or not. Returns: hamiltonian_t: An instance of the FermionOperator class. """ hamiltonian_t = None for term in hamiltonian.terms: transformed_term = None for ladder_operator in term: position_indices = grid_indices(ladder_operator[0], n_dimensions, grid_length, spinless) position_vec = position_vector(position_indices, grid_length, length_scale) new_basis = None for momentum_indices in itertools.product(range(grid_length), repeat=n_dimensions): momentum_vec = momentum_vector(momentum_indices, grid_length, length_scale) if spinless: spin = None else: spin = ladder_operator[0] % 2 orbital = orbital_id(grid_length, momentum_indices, spin) exp_index = -1.0j * numpy.dot(position_vec, momentum_vec) if ladder_operator[1] == 1: exp_index *= -1.0 element = FermionOperator(((orbital, ladder_operator[1]), ), numpy.exp(exp_index)) if new_basis is None: new_basis = element else: new_basis += element new_basis *= numpy.sqrt(1.0 / float(grid_length**n_dimensions)) if transformed_term is None: transformed_term = new_basis else: transformed_term *= new_basis if transformed_term is None: continue # Coefficient. transformed_term *= hamiltonian.terms[term] if hamiltonian_t is None: hamiltonian_t = transformed_term else: hamiltonian_t += transformed_term return hamiltonian_t
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 = momentum_vector(indices, grid) 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 = momentum_vector(indices, grid) 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] else: spins = [0, 1] for indices_a in grid.all_points_indices(): for indices_b in grid.all_points_indices(): paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. position_a = position_vector(indices_a, grid) position_b = position_vector(indices_b, grid) differences = position_b - position_a for spin_a in spins: for spin_b in spins: p = orbital_id(grid, indices_a, spin_a) q = orbital_id(grid, 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 = momentum_vector(indices_c, grid) 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)