def test_ground_state_particle_nonconserving(self): """Test getting the ground state preparation circuit for a Hamiltonian that does not conserve particle number.""" for n_qubits in self.n_qubits_range: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False, True) # Compute the true ground state sparse_operator = get_sparse_operator(quadratic_hamiltonian) ground_energy, _ = get_ground_state(sparse_operator) # Obtain the circuit circuit_description, start_orbitals = ( gaussian_state_preparation_circuit(quadratic_hamiltonian)) # Initialize the starting state state = jw_configuration_state(start_orbitals, n_qubits) # Apply the circuit particle_hole_transformation = ( jw_sparse_particle_hole_transformation_last_mode(n_qubits)) for parallel_ops in circuit_description: for op in parallel_ops: if op == 'pht': state = particle_hole_transformation.dot(state) else: i, j, theta, phi = op state = jw_sparse_givens_rotation( i, j, theta, phi, n_qubits).dot(state) # Check that the state obtained using the circuit is a ground state difference = sparse_operator * state - ground_energy * state discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def test_non_particle_conserving(self): for n_qubits in self.n_qubits_range: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False) sparse_operator = get_sparse_operator(quadratic_hamiltonian) # Diagonalize the Hamiltonian using the circuit circuit_description = reversed( quadratic_hamiltonian.diagonalizing_circuit()) particle_hole_transformation = ( jw_sparse_particle_hole_transformation_last_mode(n_qubits)) for parallel_ops in circuit_description: for op in parallel_ops: if op == 'pht': gate = particle_hole_transformation else: i, j, theta, phi = op gate = jw_sparse_givens_rotation( i, j, theta, phi, n_qubits) sparse_operator = ( gate.getH().dot(sparse_operator).dot(gate)) # Check that the result is diagonal diag = scipy.sparse.diags(sparse_operator.diagonal()) difference = sparse_operator - diag discrepancy = 0. if difference.nnz: discrepancy = max(abs(difference.data)) numpy.testing.assert_allclose(discrepancy, 0.0, atol=1e-7) # Check that the eigenvalues are in the expected order orbital_energies, constant = ( quadratic_hamiltonian.orbital_energies()) for index in range(2**n_qubits): bitstring = bin(index)[2:].zfill(n_qubits) subset = [j for j in range(n_qubits) if bitstring[j] == '1'] energy = sum([orbital_energies[j] for j in subset]) + constant self.assertAlmostEqual(sparse_operator[index, index], energy)
def jw_get_gaussian_state(quadratic_hamiltonian, occupied_orbitals=None): """Compute an eigenvalue and eigenstate of a quadratic Hamiltonian. Eigenstates of a quadratic Hamiltonian are also known as fermionic Gaussian states. Args: quadratic_hamiltonian(QuadraticHamiltonian): The Hamiltonian whose eigenstate is desired. occupied_orbitals(list): A list of integers representing the indices of the occupied orbitals in the desired Gaussian state. If this is None (the default), then it is assumed that the ground state is desired, i.e., the orbitals with negative energies are filled. Returns ------- energy (float): The eigenvalue. state (sparse): The eigenstate in scipy.sparse csc format. """ if not isinstance(quadratic_hamiltonian, QuadraticHamiltonian): raise ValueError('Input must be an instance of QuadraticHamiltonian.') n_qubits = quadratic_hamiltonian.n_qubits # Compute the energy orbital_energies, constant = quadratic_hamiltonian.orbital_energies() if occupied_orbitals is None: # The ground energy is desired if quadratic_hamiltonian.conserves_particle_number: num_negative_energies = numpy.count_nonzero( orbital_energies < -EQ_TOLERANCE) occupied_orbitals = range(num_negative_energies) else: occupied_orbitals = [] energy = numpy.sum(orbital_energies[occupied_orbitals]) + constant # Obtain the circuit that prepares the Gaussian state circuit_description, start_orbitals = \ gaussian_state_preparation_circuit(quadratic_hamiltonian, occupied_orbitals) # Initialize the starting state state = jw_configuration_state(start_orbitals, n_qubits) # Apply the circuit if not quadratic_hamiltonian.conserves_particle_number: particle_hole_transformation = ( jw_sparse_particle_hole_transformation_last_mode(n_qubits)) for parallel_ops in circuit_description: for op in parallel_ops: if op == 'pht': state = particle_hole_transformation.dot(state) else: i, j, theta, phi = op state = jw_sparse_givens_rotation(i, j, theta, phi, n_qubits).dot(state) return energy, state