def jordan_wigner_dual_basis_hamiltonian(grid, geometry=None, spinless=False, include_constant=False): """Return the dual basis Hamiltonian as QubitOperator. 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. include_constant (bool): Whether to include the Madelung constant. Returns: hamiltonian (QubitOperator) """ jellium_op = jordan_wigner_dual_basis_jellium( grid, spinless, include_constant) if geometry is None: return jellium_op for item in geometry: if len(item[1]) != grid.dimensions: raise ValueError("Invalid geometry coordinate.") if item[0] not in periodic_hash_table: raise ValueError("Invalid nuclear element.") n_orbitals = grid.num_points() volume = grid.volume_scale() if spinless: n_qubits = n_orbitals else: n_qubits = 2 * n_orbitals prefactor = -2 * numpy.pi / volume external_potential = QubitOperator() for k_indices in grid.all_points_indices(): momenta = momentum_vector(k_indices, grid) momenta_squared = momenta.dot(momenta) if momenta_squared < EQ_TOLERANCE: continue for p in range(n_qubits): index_p = grid_indices(p, grid, spinless) coordinate_p = position_vector(index_p, grid) for nuclear_term in geometry: coordinate_j = numpy.array(nuclear_term[1], float) exp_index = 1.0j * momenta.dot(coordinate_j - coordinate_p) coefficient = (prefactor / momenta_squared * periodic_hash_table[nuclear_term[0]] * numpy.exp(exp_index)) external_potential += (QubitOperator((), coefficient) - QubitOperator(((p, 'Z'),), coefficient)) return jellium_op + external_potential
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 test_momentum_vector(self): grid = Grid(dimensions=1, length=3, scale=2. * numpy.pi) test_output = [momentum_vector(i, grid) for i in range(grid.length)] correct_output = [-1., 0, 1.] self.assertEqual(correct_output, test_output) grid = Grid(dimensions=1, length=11, scale=2. * numpy.pi) for i in range(grid.length): self.assertAlmostEqual(-momentum_vector(i, grid), momentum_vector(grid.length - i - 1, grid)) # 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 += [momentum_vector((i, j), grid)] 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 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 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 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)