def test_model_integration_with_constant(self): # Compute Hamiltonian in both momentum and position space. length_scale = 0.7 for length in [2, 3]: grid = Grid(dimensions=2, length=length, scale=length_scale) spinless = True # Include Madelung constant in the momentum but not the position # Hamiltonian. momentum_hamiltonian = jellium_model(grid, spinless, True, include_constant=True) position_hamiltonian = jellium_model(grid, spinless, False) # 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 and confirm the same energy. jw_momentum = jordan_wigner(momentum_hamiltonian) jw_position = jordan_wigner(position_hamiltonian) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm momentum spectrum is shifted 2.8372/length_scale higher. max_difference = numpy.amax(momentum_spectrum - position_spectrum) min_difference = numpy.amax(momentum_spectrum - position_spectrum) self.assertAlmostEqual(max_difference, 2.8372 / length_scale) self.assertAlmostEqual(min_difference, 2.8372 / length_scale)
def setUp(self): self.x_dimension = 2 self.y_dimension = 3 # Create a Hamiltonian with nearest-neighbor hopping terms self.ferm_op = fermi_hubbard(self.x_dimension, self.y_dimension, 1., 0., 0., 0., False, True) # Get the ground energy and ground state self.ferm_op_sparse = get_sparse_operator(self.ferm_op) self.ferm_op_ground_energy, self.ferm_op_ground_state = ( get_ground_state(self.ferm_op_sparse)) # Transform the FermionOperator to a QubitOperator self.transformed_op = verstraete_cirac_2d_square( self.ferm_op, self.x_dimension, self.y_dimension, add_auxiliary_hamiltonian=True, snake=False) # Get the ground energy and state of the transformed operator self.transformed_sparse = get_sparse_operator(self.transformed_op) self.transformed_ground_energy, self.transformed_ground_state = ( get_ground_state(self.transformed_sparse))
def test_bk_jw_majoranas(self): # Check if the Majorana operators have the same spectrum # irrespectively of the transform. n_qubits = 7 a = FermionOperator(((1, 0),)) a_dag = FermionOperator(((1, 1),)) c = a + a_dag d = 1j * (a_dag - a) c_spins = [jordan_wigner(c), bravyi_kitaev_tree(c)] d_spins = [jordan_wigner(d), bravyi_kitaev_tree(d)] c_sparse = [get_sparse_operator(c_spins[0]), get_sparse_operator(c_spins[1])] d_sparse = [get_sparse_operator(d_spins[0]), get_sparse_operator(d_spins[1])] c_spectrum = [eigenspectrum(c_spins[0]), eigenspectrum(c_spins[1])] d_spectrum = [eigenspectrum(d_spins[0]), eigenspectrum(d_spins[1])] self.assertAlmostEqual(0., numpy.amax(numpy.absolute(d_spectrum[0] - d_spectrum[1])))
def test_jw_get_ground_states_by_particle_number_herm_conserving(self): # Initialize a particle-number-conserving Hermitian operator ferm_op = FermionOperator('0^ 1') + FermionOperator('1^ 0') + \ FermionOperator('1^ 2') + FermionOperator('2^ 1') + \ FermionOperator('1^ 3', -.4) + FermionOperator('3^ 1', -.4) jw_hamiltonian = jordan_wigner(ferm_op) sparse_operator = get_sparse_operator(jw_hamiltonian) n_qubits = 4 # Test each possible particle number for particle_number in range(n_qubits): # Get the ground energy and ground states at this particle number energy, states = jw_get_ground_states_by_particle_number( sparse_operator, particle_number) # Construct particle number operator num_op = get_sparse_operator(number_operator(n_qubits)) # For each vector returned, make sure that it is indeed an # eigenvector of the original operator with the returned eigenvalue # and that it has the correct particle number for vec in states: # Check that it's an eigenvector with the correct eigenvalue op_vec_product = sparse_operator.dot(vec) difference = op_vec_product - energy * vec discrepancy = 0. if difference.nnz: discrepancy = max(map(abs, difference.data)) self.assertAlmostEqual(0., discrepancy) # Check that it has the correct particle number num = expectation(num_op, vec) self.assertAlmostEqual(num, particle_number)
def test_plane_wave_hamiltonian_integration(self): length_set = [2, 3, 4] spinless_set = [True, False] length_scale = 1.1 for geometry in [[('H', (0, )), ('H', (0.8, ))], [('H', (0.1, ))], [('H', (0.1, ))]]: for l in length_set: for spinless in spinless_set: grid = Grid(dimensions=1, scale=length_scale, length=l) h_plane_wave = plane_wave_hamiltonian( grid, geometry, spinless, True, include_constant=False) h_dual_basis = plane_wave_hamiltonian( grid, geometry, spinless, False, include_constant=False) # Test for Hermiticity plane_wave_operator = get_sparse_operator(h_plane_wave) dual_operator = get_sparse_operator(h_dual_basis) self.assertTrue(is_hermitian((plane_wave_operator))) self.assertTrue(is_hermitian(dual_operator)) jw_h_plane_wave = jordan_wigner(h_plane_wave) jw_h_dual_basis = jordan_wigner(h_dual_basis) h_plane_wave_spectrum = eigenspectrum(jw_h_plane_wave) h_dual_basis_spectrum = eigenspectrum(jw_h_dual_basis) max_diff = numpy.amax(h_plane_wave_spectrum - h_dual_basis_spectrum) min_diff = numpy.amin(h_plane_wave_spectrum - h_dual_basis_spectrum) self.assertAlmostEqual(max_diff, 0) self.assertAlmostEqual(min_diff, 0)
def setUp(self): self.n_qubits = 5 self.constant = 1.7 self.chemical_potential = 2. # Obtain random Hermitian and antisymmetric matrices self.hermitian_mat = random_hermitian_matrix(self.n_qubits) self.antisymmetric_mat = random_antisymmetric_matrix(self.n_qubits) self.combined_hermitian = ( self.hermitian_mat - self.chemical_potential * numpy.eye(self.n_qubits)) # Initialize a particle-number-conserving Hamiltonian self.quad_ham_pc = QuadraticHamiltonian(self.hermitian_mat, constant=self.constant) # Initialize a non-particle-number-conserving Hamiltonian self.quad_ham_npc = QuadraticHamiltonian(self.hermitian_mat, self.antisymmetric_mat, self.constant, self.chemical_potential) # Initialize the sparse operators and get their ground energies self.quad_ham_pc_sparse = get_sparse_operator(self.quad_ham_pc) self.quad_ham_npc_sparse = get_sparse_operator(self.quad_ham_npc) self.pc_ground_energy, self.pc_ground_state = get_ground_state( self.quad_ham_pc_sparse) self.npc_ground_energy, self.npc_ground_state = get_ground_state( self.quad_ham_npc_sparse)
def get_exact_gs(self, hamiltonian=None): """ Calculate the exact groundstate energy of the loaded Hamiltonian :param PauliSum hamiltonian: (optional) Hamiltonian of which one would like to calculate the GS energy :return: groundstate energy :rtype: float """ if hamiltonian is None: h = get_sparse_operator( pyquilpauli_to_qubitop(PauliSum(self.pauli_list))) else: if isinstance(hamiltonian, PauliSum): h = get_sparse_operator(pyquilpauli_to_qubitop(hamiltonian)) else: raise TypeError('please give a PauliSum()' ' object as the Hamiltonian.') if h.shape[0] > 1024: # sparse matrix eigenvalue decomposition is less accurate, # but is necessary for larger matrices [w, _] = sp.sparse.linalg.eigsh(h, k=1) else: [w, _] = np.linalg.eigh(h.todense()) Egs = min(w).real return Egs
def test_potential_integration(self): # Compute potential energy operator in momentum and position space. for length in [2, 3]: grid = Grid(dimensions=2, length=length, scale=2.) spinless = True momentum_potential = plane_wave_potential(grid, spinless) position_potential = dual_basis_potential(grid, spinless) # Confirm they are Hermitian momentum_potential_operator = ( get_sparse_operator(momentum_potential)) self.assertTrue(is_hermitian(momentum_potential_operator)) position_potential_operator = ( get_sparse_operator(position_potential)) self.assertTrue(is_hermitian(position_potential_operator)) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_potential) jw_position = jordan_wigner(position_potential) 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_model_integration(self): # Compute Hamiltonian in both momentum and position space. for length in [2, 3]: grid = Grid(dimensions=2, length=length, scale=1.0) spinless = True momentum_hamiltonian = jellium_model(grid, spinless, True) position_hamiltonian = jellium_model(grid, spinless, False) # 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 and confirm the same energy. 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_nonperiodic_external_potential_integration(self): # Compute potential energy operator in momentum and position space. # Non-periodic test. ''' Key: Spatial dimension. Value: List of geometries. ''' geometry_sets = { 2: [[('H', (0., 0.)), ('H', (0.8, 0.))], [('H', (0.1, 0.))]], 3: [[('H', (0., 0., 0.)), ('H', (0.8, 0., 0.))], [('H', (0.1, 0., 0.))]] } # [[spatial dimension, fieldline dimension]] dims = [[2, 2], [2, 3], [3, 3]] scale = 8 * 1. spinless = True 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 geometry in geometry_sets[dim[0]]: grid = Grid(dimensions=dim[0], scale=scale, length=length) period_cutoff = grid.volume_scale() ** (1. / grid.dimensions) momentum_external_potential = plane_wave_external_potential(grid, geometry, spinless, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) position_external_potential = dual_basis_external_potential(grid, geometry, spinless, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) # Confirm they are Hermitian momentum_external_potential_operator = ( get_sparse_operator(momentum_external_potential)) self.assertTrue(is_hermitian(momentum_external_potential_operator)) position_external_potential_operator = ( get_sparse_operator(position_external_potential)) self.assertTrue(is_hermitian(position_external_potential_operator)) # Diagonalize. jw_momentum = jordan_wigner(momentum_external_potential) jw_position = jordan_wigner(position_external_potential) 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_apply_constraints(self): # Get norm of original operator. original_norm = 0. for term, coefficient in self.fermion_hamiltonian.terms.items(): if term != (): original_norm += abs(coefficient) # Get modified operator. modified_operator = apply_constraints(self.fermion_hamiltonian, self.n_fermions) modified_operator.compress() # Get norm of modified operator. modified_norm = 0. for term, coefficient in modified_operator.terms.items(): if term != (): modified_norm += abs(coefficient) self.assertTrue(modified_norm < original_norm) # Map both to sparse matrix under Jordan-Wigner. sparse_original = get_sparse_operator(self.fermion_hamiltonian) sparse_modified = get_sparse_operator(modified_operator) # Check spectra. sparse_original = jw_number_restrict_operator(sparse_original, self.n_fermions) sparse_modified = jw_number_restrict_operator(sparse_modified, self.n_fermions) original_spectrum = sparse_eigenspectrum(sparse_original) modified_spectrum = sparse_eigenspectrum(sparse_modified) spectral_deviation = numpy.amax( numpy.absolute(original_spectrum - modified_spectrum)) self.assertAlmostEqual(spectral_deviation, 0.) # Check expectation value. energy, wavefunction = get_ground_state(sparse_original) modified_energy = expectation(sparse_modified, wavefunction) self.assertAlmostEqual(modified_energy, energy) # Test consistency with cvxopt. scipy_operator = apply_constraints(self.fermion_hamiltonian, self.n_fermions, use_scipy=False) # Get norm. scipy_norm = 0. for term, coefficient in scipy_operator.terms.items(): if term != (): scipy_norm += abs(coefficient) self.assertAlmostEqual(scipy_norm, modified_norm)
def test_model_integration_with_constant(self): # Compute Hamiltonian in both momentum and position space. length_scale = 0.7 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=length_scale) # Include Madelung constant in the momentum but not the position # Hamiltonian. momentum_hamiltonian = jellium_model(grid, spinless, True, include_constant=True, fieldlines=dim[1]) position_hamiltonian = jellium_model(grid, spinless, False, 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 and confirm the same energy. jw_momentum = jordan_wigner(momentum_hamiltonian) jw_position = jordan_wigner(position_hamiltonian) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm momentum spectrum is shifted 2.8372/length_scale higher. max_difference = numpy.amax(momentum_spectrum - position_spectrum) min_difference = numpy.amax(momentum_spectrum - position_spectrum) self.assertAlmostEqual(max_difference, 2.8372 / length_scale) self.assertAlmostEqual(min_difference, 2.8372 / length_scale)
def test_nonperiodic_potential_integration(self): # Compute potential energy operator in momentum and position space. # Non-periodic 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.) period_cutoff = grid.volume_scale()**(1. / grid.dimensions) / 2. momentum_potential = plane_wave_potential( grid, spinless=spinless, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) position_potential = dual_basis_potential( grid, spinless=spinless, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) # Confirm they are Hermitian momentum_potential_operator = ( get_sparse_operator(momentum_potential)) self.assertTrue(is_hermitian(momentum_potential_operator)) position_potential_operator = ( get_sparse_operator(position_potential)) self.assertTrue(is_hermitian(position_potential_operator)) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_potential) jw_position = jordan_wigner(position_potential) 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_circuit_generation_and_accuracy(): for dim in range(2, 10): qubits = cirq.LineQubit.range(dim) u_generator = numpy.random.random( (dim, dim)) + 1j * numpy.random.random((dim, dim)) u_generator = u_generator - numpy.conj(u_generator).T assert numpy.allclose(-1 * u_generator, numpy.conj(u_generator).T) unitary = scipy.linalg.expm(u_generator) circuit = cirq.Circuit() circuit.append(optimal_givens_decomposition(qubits, unitary)) fermion_generator = QubitOperator(()) * 0.0 for i, j in product(range(dim), repeat=2): fermion_generator += jordan_wigner( FermionOperator(((i, 1), (j, 0)), u_generator[i, j])) true_unitary = scipy.linalg.expm( get_sparse_operator(fermion_generator).toarray()) assert numpy.allclose(true_unitary.conj().T.dot(true_unitary), numpy.eye(2 ** dim, dtype=complex)) test_unitary = cirq.unitary(circuit) assert numpy.isclose( abs(numpy.trace(true_unitary.conj().T.dot(test_unitary))), 2 ** dim)
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 = 0. if difference.nnz: discrepancy = max(abs(difference.data)) self.assertTrue(discrepancy < EQ_TOLERANCE)
def __init__(self, L: int, J: float, B: float, sparse=False, qubits: Sequence[cirq.Qid] = None): ''' TODO: TFIMChain.__init__ documentation ''' self.L = L self.J = J self.B = B if L <= 2 and sparse: print(f'! sparse encoding unavailable for {L} qubit system.\n' '! setting self.sparse = False.') sparse = False ground_state_degeneracy = 2 if J > B else 1 if qubits is None: qubits = [cirq.LineQubit(i) for i in range(L)] else: qubits = qubits self.qubit_ham = ops.QubitOperator('X0', -B) for i in range(L - 1): self.qubit_ham += ops.QubitOperator(f'X{i+1}', -B) self.qubit_ham += ops.QubitOperator(f'Z{i} Z{i+1}', -J) sparse_ham = transforms.get_sparse_operator(self.qubit_ham) super().__init__(sparse_ham, qubits, ground_state_degeneracy, sparse)
def expectation(qubit_op, wavefunction, reverse_operator=True): """Get the expectation value of a qubit operator with respect to a wavefunction. Args: qubit_op (openfermion.ops.QubitOperator): the operator wavefunction (pyquil.wavefunction.Wavefunction): the wavefunction reverse_operator (boolean): whether to reverse order of qubit operator before computing expectation value. This should be True if the convention of the basis states used for the wavefunction is the opposite of the one in the qubit operator. This is the case, e.g. when the wavefunction comes from Pyquil. Returns: complex: the expectation value """ n_qubits = wavefunction.amplitudes.shape[0].bit_length() - 1 # Convert the qubit operator to a sparse matrix. Note that the qubit indices # must be reversed because OpenFermion and pyquil use different conventions # for how to order the computational basis states! if reverse_operator: qubit_op = reverse_qubit_order(qubit_op, n_qubits=n_qubits) sparse_op = get_sparse_operator(qubit_op, n_qubits=n_qubits) # Computer the expectation value exp_val = openfermion_expectation(sparse_op, wavefunction.amplitudes) return exp_val
def eigenspectrum(operator, n_qubits=None): """Compute the eigenspectrum of an operator. WARNING: This function has cubic runtime in dimension of Hilbert space operator, which might be exponential. NOTE: This function does not currently support QuadOperator and BosonOperator. Args: operator: QubitOperator, InteractionOperator, FermionOperator, PolynomialTensor, or InteractionRDM. n_qubits (int): number of qubits/modes in operator. if None, will be counted. Returns: spectrum: dense numpy array of floats giving eigenspectrum. """ if isinstance(operator, (QuadOperator, BosonOperator)): raise TypeError('Operator of invalid type.') from openfermion.transforms import get_sparse_operator from openfermion.utils import sparse_eigenspectrum sparse_operator = get_sparse_operator(operator, n_qubits) spectrum = sparse_eigenspectrum(sparse_operator) return spectrum
def test_simulate_overlapping_number_and_hopping_terms(self): hamiltonian = (0.37 * FermionOperator('1^ 0^ 1 0') + FermionOperator('2^ 0') + FermionOperator('0^ 2')) projectq.ops.All(projectq.ops.H) | self.register _low_depth_trotter_simulation.simulate_dual_basis_evolution( self.register, hamiltonian, trotter_steps=2, first_order=False) self.engine.flush() # get_sparse_operator reverses the indices, so reverse the sites # the Hamiltonian acts on so as to compare them. reversed_operator = (0.37 * FermionOperator('3^ 2^ 3 2') + FermionOperator('3^ 1') + FermionOperator('1^ 3')) evol_matrix = expm(-1j * get_sparse_operator( reversed_operator, n_qubits=self.size)).todense() expected = evol_matrix * numpy.matrix( [2**(-self.size / 2.)] * 2**self.size).T self.assertTrue( numpy.allclose(ordered_wavefunction(self.engine), expected.T, atol=1e-2), msg=str( numpy.array(ordered_wavefunction(self.engine) - expected.T)))
def test_hf_state_plane_wave_basis_lowest_single_determinant_state(self): grid_length = 7 dimension = 1 spinless = True n_particles = 4 length_scale = 2.0 grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless) hamiltonian_sparse = get_sparse_operator(hamiltonian) hf_state = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=True) HF_energy = expectation(hamiltonian_sparse, hf_state) for occupied_orbitals in permutations([1] * n_particles + [0] * (grid_length - n_particles)): state_index = numpy.sum(2**numpy.array(occupied_orbitals)) HF_competitor = numpy.zeros(2**grid_length) HF_competitor[state_index] = 1.0 self.assertLessEqual( HF_energy, expectation(hamiltonian_sparse, HF_competitor))
def test_simulate_dual_basis_hamiltonian(self): hamiltonian = dual_basis_hamiltonian(1, self.size) self.engine.flush() # Choose random state. initial_state = numpy.zeros(2**self.size, dtype=complex) for i in range(len(initial_state)): initial_state[i] = (random.random() * numpy.exp(1j * 2 * numpy.pi * random.random())) initial_state /= numpy.linalg.norm(initial_state) # Put randomly chosen state in the registers. self.engine.backend.set_wavefunction(initial_state, self.register) _low_depth_trotter_simulation.simulate_dual_basis_evolution( self.register, hamiltonian, trotter_steps=7, first_order=False) self.engine.flush() # get_sparse_operator reverses the indices, but the jellium # Hamiltonian is symmetric. evol_matrix = expm( -1j * get_sparse_operator(hamiltonian, n_qubits=self.size)).todense() initial_state = numpy.matrix(initial_state).T expected = evol_matrix * initial_state self.assertTrue( numpy.allclose(ordered_wavefunction(self.engine), expected.T, atol=1e-2), msg=str( numpy.array(ordered_wavefunction(self.engine) - expected.T)))
def test_circuit_generation_state(): """ Determine if we rotate the Hartree-Fock state correctly """ simulator = cirq.Simulator() circuit = cirq.Circuit() qubits = cirq.LineQubit.range(4) circuit.append([cirq.X(qubits[0]), cirq.X(qubits[1]), cirq.X(qubits[1]), cirq.X(qubits[2]), cirq.X(qubits[3]), cirq.X(qubits[3])]) # alpha-spins are first then beta spins wavefunction = numpy.zeros((2 ** 4, 1), dtype=complex) wavefunction[10, 0] = 1.0 dim = 2 u_generator = numpy.random.random((dim, dim)) + 1j * numpy.random.random( (dim, dim)) u_generator = u_generator - numpy.conj(u_generator).T unitary = scipy.linalg.expm(u_generator) circuit.append(optimal_givens_decomposition(qubits[:2], unitary)) fermion_generator = QubitOperator(()) * 0.0 for i, j in product(range(dim), repeat=2): fermion_generator += jordan_wigner( FermionOperator(((i, 1), (j, 0)), u_generator[i, j])) test_unitary = scipy.linalg.expm( get_sparse_operator(fermion_generator, 4).toarray()) test_final_state = test_unitary.dot(wavefunction) cirq_wf = simulator.simulate(circuit).final_state assert numpy.allclose(cirq_wf, test_final_state.flatten())
def _test_mat_to_op(hamiltonian_operator, jmin=0.5, jmax=100, tol=1e-8): """ Tests that eigs computed using a function that generates hamiltonian operators are correct. This test doesn't check for extra eigs but only that the ones that should be there is there. @author: kajoel """ from core.lipkin_quasi_spin import hamiltonian, eigs from openfermion.transforms import get_sparse_operator no_error = True for j2 in range(round(2 * jmin), round(2 * jmax) + 1): j = j2 / 2 print("j = " + str(j)) V = float(np.random.randn(1)) H = hamiltonian(j, V) E = eigs(j, V) for i in range(len(H)): H_op = hamiltonian_operator(H[i]) H_op = get_sparse_operator(H_op).toarray() E_op = np.linalg.eigvals(H_op) # Check that E_op contains all eigs in E[i] for E_ in E[i]: if all(abs(E_op - E_) > tol): no_error = False print("Max diff: " + str(max(abs(E_op - E_)))) if no_error: print("Success!") else: print("Fail!")
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, ground_state = 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 = 0. if difference.nnz: discrepancy = max(abs(difference.data)) self.assertTrue(discrepancy < EQ_TOLERANCE)
def test_hf_state_energy_close_to_ground_energy_at_high_density(self): grid_length = 8 dimension = 1 spinless = True n_particles = grid_length**dimension // 2 # High density -> small length_scale. length_scale = 0.25 grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless) hamiltonian_sparse = get_sparse_operator(hamiltonian) hf_state = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=True) restricted_hamiltonian = jw_number_restrict_operator( hamiltonian_sparse, n_particles) E_g = get_ground_state(restricted_hamiltonian)[0] E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state) self.assertAlmostEqual(E_g, E_HF_plane_wave, places=5)
def continuous_logsweep_protocol(init_state: np.ndarray, system: QuantumSimulatedSystem, n_energy_steps: int, mode: str = 'auto') -> np.ndarray: ''' Runs the continuous-evolution QDC LogSweep protocol on the state init_state of a QuantumSimulatedSystem. For details and caveats, see `qdcnumpy.continuous_energy_sweep_protocol`. Couplings are XX, XY, XZ on each of the system's qubits in sequence. Energies and linewidths are chosen according to: epsilon[0] == e_max epsilon[n_energy_steps] == e_min epsilon[k] - epsilon[k+1] == (delta[k] + delta[k+1]) / delta_factor Where delta_factor is optimized as indicated in appendix B of the paper. Args: init_state: density matrix, state vector or matrix of state coulumn-vectors of the initial state. system: object containing Hamiltonian and simulation data. n_energy_steps: energy gradation number K. mode: Whether to use density matrix or wavefunction simulation. If set to 'auto' (default), the choice will be made depending on the input type (square matrix -> 'DM', vector or rectangular matrix -> 'WF') Returns: state (density matrix) after the application of the protocol Raises: ValueError: if the input state has the wrong form for the mode, or if the input system is sparse-encoded TypeError: if the input state is not a np.ndarray ''' L = len(system.get_qubits()) coupl_potentials = [ transforms.get_sparse_operator(ops.QubitOperator((i, P), ), n_qubits=L) for i in range(L) for P in ('X', 'Y', 'Z') ] e_max_transitions = max( perp_norm(cp, system.get_sparse_hamiltonian()) for cp in coupl_potentials) system.eig() # define e_min, e_max using perp_norm on sparse matrices e_min = system.ground_state_gap() e_max = e_max_transitions # define delta_factor delta_factor = opt_delta_factor(e_min, e_max, n_energy_steps) epsilon_list, delta_list = logsweep_params(e_min, e_max, n_energy_steps, delta_factor) return continuous_energy_sweep_protocol(init_state=init_state, system=system, couplings=['X', 'Y', 'Z'], epsilon_list=epsilon_list, delta_list=delta_list, mode=mode)
def generator(x: List[float]): # Must provide imports in the function! from scipy.sparse.linalg import expm from openfermion.ops import QubitOperator from openfermion.transforms import get_sparse_operator qop = QubitOperator('X0 Y1') - QubitOperator('Y0 X1') qubit_sparse = get_sparse_operator(qop) return expm(0.5j * x[0] * qubit_sparse).todense()
def test_plane_wave_hamiltonian_integration(self): ''' Key: Spatial dimension. Value: List of geometries. ''' geometry_sets = { 2: [[('H', (0., 0.)), ('H', (0.8, 0.))], [('H', (0.1, 0.))]], 3: [[('H', (0., 0., 0.)), ('H', (0.8, 0., 0.))], [('H', (0.1, 0., 0.))]] } # [[spatial dimension, fieldline dimension]] dims = [[2, 2], [2, 3], [3, 3]] length_scale = 1.1 spinless = True 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 geometry in geometry_sets[dim[0]]: grid = Grid(dimensions=dim[0], scale=length_scale, length=2) h_plane_wave = plane_wave_hamiltonian( grid, geometry, spinless, True, include_constant=False, fieldlines=dim[1]) h_dual_basis = plane_wave_hamiltonian( grid, geometry, spinless, False, include_constant=False, fieldlines=dim[1]) # Test for Hermiticity plane_wave_operator = get_sparse_operator(h_plane_wave) dual_operator = get_sparse_operator(h_dual_basis) self.assertTrue(is_hermitian((plane_wave_operator))) self.assertTrue(is_hermitian(dual_operator)) jw_h_plane_wave = jordan_wigner(h_plane_wave) jw_h_dual_basis = jordan_wigner(h_dual_basis) h_plane_wave_spectrum = eigenspectrum(jw_h_plane_wave) h_dual_basis_spectrum = eigenspectrum(jw_h_dual_basis) max_diff = numpy.amax( h_plane_wave_spectrum - h_dual_basis_spectrum) min_diff = numpy.amin( h_plane_wave_spectrum - h_dual_basis_spectrum) self.assertAlmostEqual(max_diff, 0) self.assertAlmostEqual(min_diff, 0)
def test_get_ground_states_by_particle_number_nonhermitian(self): # Initialize a non-Hermitian operator ferm_op = FermionOperator('0^ 1') + FermionOperator('2^ 1') jw_hamiltonian = jordan_wigner(ferm_op) sparse_operator = get_sparse_operator(jw_hamiltonian) with self.assertRaises(ValueError): jw_get_ground_states_by_particle_number(sparse_operator, 0)