def test_random_operators_are_reproducible(self): op1 = random_diagonal_coulomb_hamiltonian(5, seed=5947) op2 = random_diagonal_coulomb_hamiltonian(5, seed=5947) numpy.testing.assert_allclose(op1.one_body, op2.one_body) numpy.testing.assert_allclose(op1.two_body, op2.two_body) op1 = random_interaction_operator(5, seed=8911) op2 = random_interaction_operator(5, seed=8911) numpy.testing.assert_allclose(op1.one_body_tensor, op2.one_body_tensor) numpy.testing.assert_allclose(op1.two_body_tensor, op2.two_body_tensor) op1 = random_quadratic_hamiltonian(5, seed=17711) op2 = random_quadratic_hamiltonian(5, seed=17711) numpy.testing.assert_allclose(op1.combined_hermitian_part, op2.combined_hermitian_part) numpy.testing.assert_allclose(op1.antisymmetric_part, op2.antisymmetric_part) op1 = random_antisymmetric_matrix(5, seed=24074) op2 = random_antisymmetric_matrix(5, seed=24074) numpy.testing.assert_allclose(op1, op2) op1 = random_hermitian_matrix(5, seed=56753) op2 = random_hermitian_matrix(5, seed=56753) numpy.testing.assert_allclose(op1, op2) op1 = random_unitary_matrix(5, seed=56486) op2 = random_unitary_matrix(5, seed=56486) numpy.testing.assert_allclose(op1, op2)
def test_diagonalizing_bogoliubov_transform_exceptions(self): quad_ham = random_quadratic_hamiltonian(5) with self.assertRaises(ValueError): _ = quad_ham.diagonalizing_bogoliubov_transform(spin_sector=0) quad_ham = random_quadratic_hamiltonian( 5, conserves_particle_number=False, expand_spin=True) with self.assertRaises(NotImplementedError): _ = quad_ham.diagonalizing_bogoliubov_transform(spin_sector=0)
def test_random_quadratic(self): n_qubits = 5 quad_ham = random_quadratic_hamiltonian(n_qubits, True) ferm_op = get_fermion_operator(quad_ham) self.assertTrue( jordan_wigner(ferm_op) == jordan_wigner( get_diagonal_coulomb_hamiltonian(ferm_op)))
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_excited_state_particle_nonconserving(self): """Test getting an excited state of a Hamiltonian that conserves particle number.""" for n_qubits in self.n_qubits_range: # Initialize a non-particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False) # Pick some orbitals to occupy num_occupied_orbitals = numpy.random.randint(1, n_qubits + 1) occupied_orbitals = numpy.random.choice(range(n_qubits), num_occupied_orbitals, False) # Compute the Gaussian state circuit_energy, gaussian_state = jw_get_gaussian_state( quadratic_hamiltonian, occupied_orbitals) # Compute the true energy orbital_energies, constant = ( quadratic_hamiltonian.orbital_energies()) energy = numpy.sum(orbital_energies[occupied_orbitals]) + constant # Check that the energies match self.assertAlmostEqual(energy, circuit_energy) # Check that the state obtained using the circuit is an eigenstate # with the correct eigenvalue sparse_operator = get_sparse_operator(quadratic_hamiltonian) difference = (sparse_operator * gaussian_state - energy * gaussian_state) discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def test_particle_conserving(self): for n_qubits in self.n_qubits_range: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, True) sparse_operator = get_sparse_operator(quadratic_hamiltonian) # Diagonalize the Hamiltonian using the circuit circuit_description = reversed( quadratic_hamiltonian.diagonalizing_circuit()) for parallel_ops in circuit_description: for op in parallel_ops: 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 test_not_implemented_spinr_reduced(): """Tests that currently un-implemented functionality is caught.""" msg = "Specifying spin sector for non-particle-conserving " msg += "Hamiltonians is not yet supported." for n_qubits in [2, 4, 6]: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False, True) # Obtain the circuit with pytest.raises(NotImplementedError): _ = gaussian_state_preparation_circuit(quadratic_hamiltonian, spin_sector=1)
def test_fqe_givens(): """Test Givens Rotation evolution for correctness.""" # set up norbs = 4 n_elec = norbs sz = 0 n_qubits = 2 * norbs time = 0.126 fqe_wfn = fqe.Wavefunction([[n_elec, sz, norbs]]) fqe_wfn.set_wfn(strategy="random") ikappa = random_quadratic_hamiltonian( norbs, conserves_particle_number=True, real=False, expand_spin=False, seed=2, ) fqe_ham = RestrictedHamiltonian((ikappa.n_body_tensors[1, 0], )) u = expm(-1j * ikappa.n_body_tensors[1, 0] * time) # time-evolve final_fqe_wfn = fqe_wfn.time_evolve(time, fqe_ham) spin_ham = np.kron(ikappa.n_body_tensors[1, 0], np.eye(2)) assert of.is_hermitian(spin_ham) ikappa_spin = of.InteractionOperator( constant=0, one_body_tensor=spin_ham, two_body_tensor=np.zeros((n_qubits, n_qubits, n_qubits, n_qubits)), ) bigU = expm(-1j * of.get_sparse_operator(ikappa_spin).toarray() * time) initial_wf = fqe.to_cirq(fqe_wfn).reshape((-1, 1)) final_wf = bigU @ initial_wf final_wfn_test = fqe.from_cirq(final_wf.flatten(), 1.0e-12) assert np.allclose(final_fqe_wfn.rdm("i^ j"), final_wfn_test.rdm("i^ j")) assert np.allclose(final_fqe_wfn.rdm("i^ j^ k l"), final_wfn_test.rdm("i^ j^ k l")) final_wfn_test2 = fqe.from_cirq( evolve_wf_givens(initial_wf.copy(), u.copy()).flatten(), 1.0e-12) givens_fqe_wfn = evolve_fqe_givens(fqe_wfn, u.copy()) assert np.allclose(givens_fqe_wfn.rdm("i^ j"), final_wfn_test2.rdm("i^ j")) assert np.allclose(givens_fqe_wfn.rdm("i^ j^ k l"), final_wfn_test2.rdm("i^ j^ k l"))
def test_ground_state_particle_nonconserving(self): """Test getting the ground state of a Hamiltonian that does not conserve particle number.""" for n_qubits in self.n_qubits_range: # Initialize a non-particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False) # Compute the true ground state sparse_operator = get_sparse_operator(quadratic_hamiltonian) ground_energy, _ = get_ground_state(sparse_operator) # Compute the ground state using the circuit circuit_energy, circuit_state = ( jw_get_gaussian_state(quadratic_hamiltonian)) # Check that the energies match self.assertAlmostEqual(ground_energy, circuit_energy) # Check that the state obtained using the circuit is a ground state difference = (sparse_operator * circuit_state - ground_energy * circuit_state) discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def test_diagonalizing_bogoliubov_transform_particle_conserving(self): """Test particle-conserving diagonalizing Bogoliubov transform.""" # Spin-symmetric quad_ham = random_quadratic_hamiltonian(5, conserves_particle_number=True, expand_spin=True) quad_ham = get_quadratic_hamiltonian( reorder(get_fermion_operator(quad_ham), up_then_down)) orbital_energies, transformation_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform()) max_upper_right = numpy.max(numpy.abs(transformation_matrix[:5, 5:])) max_lower_left = numpy.max(numpy.abs(transformation_matrix[5:, :5])) numpy.testing.assert_allclose(orbital_energies[:5], orbital_energies[5:]) numpy.testing.assert_allclose(transformation_matrix.dot( quad_ham.combined_hermitian_part.T.dot( transformation_matrix.T.conj())), numpy.diag(orbital_energies), atol=1e-7) numpy.testing.assert_allclose(max_upper_right, 0.0) numpy.testing.assert_allclose(max_lower_left, 0.0) # Specific spin sector quad_ham = random_quadratic_hamiltonian(5, conserves_particle_number=True, expand_spin=True) quad_ham = get_quadratic_hamiltonian( reorder(get_fermion_operator(quad_ham), up_then_down)) for spin_sector in range(2): orbital_energies, transformation_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform( spin_sector=spin_sector)) def index_map(i): return i + spin_sector * 5 spin_indices = [index_map(i) for i in range(5)] spin_matrix = quad_ham.combined_hermitian_part[numpy.ix_( spin_indices, spin_indices)] numpy.testing.assert_allclose(transformation_matrix.dot( spin_matrix.T.dot(transformation_matrix.T.conj())), numpy.diag(orbital_energies), atol=1e-7) # Not spin-symmetric quad_ham = random_quadratic_hamiltonian(5, conserves_particle_number=True, expand_spin=False) orbital_energies, _ = quad_ham.orbital_energies() _, transformation_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform()) numpy.testing.assert_allclose(transformation_matrix.dot( quad_ham.combined_hermitian_part.T.dot( transformation_matrix.T.conj())), numpy.diag(orbital_energies), atol=1e-7)
def test_main_procedure(self): for n in self.test_dimensions: # Obtain a random quadratic Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian(n) # Get the diagonalizing transformation _, transformation_matrix, _ = ( quadratic_hamiltonian.diagonalizing_bogoliubov_transform()) left_block = transformation_matrix[:, :n] right_block = transformation_matrix[:, n:] lower_unitary = numpy.empty((n, 2 * n), dtype=complex) lower_unitary[:, :n] = numpy.conjugate(right_block) lower_unitary[:, n:] = numpy.conjugate(left_block) # Get fermionic Gaussian decomposition of lower_unitary decomposition, left_decomposition, diagonal, left_diagonal = ( fermionic_gaussian_decomposition(lower_unitary)) # Compute left_unitary left_unitary = numpy.eye(n, dtype=complex) for parallel_set in left_decomposition: combined_op = numpy.eye(n, dtype=complex) for op in reversed(parallel_set): i, j, theta, phi = op c = numpy.cos(theta) s = numpy.sin(theta) phase = numpy.exp(1.j * phi) givens_rotation = numpy.array( [[c, -phase * s], [s, phase * c]], dtype=complex) givens_rotate(combined_op, givens_rotation, i, j) left_unitary = combined_op.dot(left_unitary) for i in range(n): left_unitary[i] *= left_diagonal[i] left_unitary = left_unitary.T for i in range(n): left_unitary[i] *= diagonal[i] # Check that left_unitary zeroes out the correct entries of # lower_unitary product = left_unitary.dot(lower_unitary) for i in range(n - 1): for j in range(n - 1 - i): self.assertAlmostEqual(product[i, j], 0.) # Compute right_unitary right_unitary = numpy.eye(2 * n, dtype=complex) for parallel_set in decomposition: combined_op = numpy.eye(2 * n, dtype=complex) for op in reversed(parallel_set): if op == 'pht': swap_rows(combined_op, n - 1, 2 * n - 1) else: i, j, theta, phi = op c = numpy.cos(theta) s = numpy.sin(theta) phase = numpy.exp(1.j * phi) givens_rotation = numpy.array( [[c, -phase * s], [s, phase * c]], dtype=complex) double_givens_rotate(combined_op, givens_rotation, i, j) right_unitary = combined_op.dot(right_unitary) # Compute left_unitary * lower_unitary * right_unitary^\dagger product = left_unitary.dot( lower_unitary.dot(right_unitary.T.conj())) # Construct the diagonal matrix diag = numpy.zeros((n, 2 * n), dtype=complex) diag[range(n), range(n, 2 * n)] = diagonal # Assert that W and D are the same for i in numpy.ndindex((n, 2 * n)): self.assertAlmostEqual(diag[i], product[i])