def _potential_to_quadratic_hamiltonian( potential: np.ndarray, j: Union[Real, Iterable[Real]]) -> openfermion.QuadraticHamiltonian: sites_count = len(potential) if isinstance(j, Iterable): j = np.array(j) else: j = np.full(sites_count - 1, j) if len(j) != sites_count - 1: raise ValueError( 'Hopping coefficient size incompatible with potential') # Prepare one-body matrix T_ij. one_body = np.zeros((sites_count, sites_count)) # Nearest-neighbor hopping terms. for i in range(sites_count - 1): one_body[i, i + 1] += -j[i] one_body[i + 1, i] += -j[i] # Local interaction terms. for i in range(sites_count): one_body[i, i] += potential[i] return openfermion.QuadraticHamiltonian(one_body)
def kevin_hubbard_cirq(self): # Create Hubbard model Hamiltonian # -------------------------------- x_dim = 3 y_dim = 2 n_sites = x_dim * y_dim n_modes = 2 * n_sites tunneling = 1.0 coulomb = 4.0 hubbard_model = of.fermi_hubbard(x_dim, y_dim, tunneling, coulomb) # Reorder indices hubbard_model = of.reorder(hubbard_model, of.up_then_down) # Convert to DiagonalCoulombHamiltonian hubbard_hamiltonian = of.get_diagonal_coulomb_hamiltonian( hubbard_model) # Create qubits qubits = cirq.LineQubit.range(n_modes) # State preparation circuit for eigenstate of one-body term # --------------------------------------------------------- # Set the pseudo-particle orbitals to fill up_orbitals = range(n_sites // 2) down_orbitals = range(n_sites // 2) # Create the circuit hubbard_state_preparation_circuit = cirq.Circuit.from_ops( ofc.prepare_gaussian_state( qubits, of.QuadraticHamiltonian(hubbard_hamiltonian.one_body), occupied_orbitals=(up_orbitals, down_orbitals)), strategy=cirq.InsertStrategy.EARLIEST) # Trotter simulation circuit # -------------------------- n_steps = 10 order = 0 hubbard_simulation_circuit = cirq.Circuit.from_ops( ofc.simulate_trotter(qubits, hubbard_hamiltonian, time=1.0, n_steps=n_steps, order=order, algorithm=ofc.trotter.LINEAR_SWAP_NETWORK), strategy=cirq.InsertStrategy.EARLIEST) t0 = time.time() self.kevin_optimize_circuit(hubbard_state_preparation_circuit) self.kevin_optimize_circuit(hubbard_simulation_circuit) t1 = time.time() # print('Optimizing circuits took {} seconds'.format(t1 - t0)) # print(hubbard_state_preparation_circuit.to_text_diagram(transpose=True)) return hubbard_state_preparation_circuit, hubbard_simulation_circuit
def test_trotter_ansatzes_default_initial_params_iterations_1( ansatz_factory, trotter_algorithm, order, hamiltonian, atol): """Check that a Trotter ansatz with one iteration and default parameters is consistent with time evolution with one Trotter step.""" ansatz = ansatz_factory(hamiltonian, iterations=1) objective = HamiltonianObjective(hamiltonian) qubits = ansatz.qubits if isinstance(hamiltonian, openfermion.DiagonalCoulombHamiltonian): one_body = hamiltonian.one_body elif isinstance(hamiltonian, openfermion.InteractionOperator): one_body = hamiltonian.one_body_tensor preparation_circuit = cirq.Circuit.from_ops( prepare_gaussian_state(qubits, openfermion.QuadraticHamiltonian(one_body), occupied_orbitals=range(len(qubits) // 2))) # Compute value using ansatz circuit and objective circuit = (preparation_circuit + ansatz.circuit).with_parameters_resolved_by( ansatz.param_resolver(ansatz.default_initial_params())) result = circuit.apply_unitary_effect_to_state( qubit_order=ansatz.qubit_permutation(qubits)) obj_val = objective.value(result) # Compute value using study study = VariationalStudy('study', ansatz, objective, preparation_circuit=preparation_circuit) study_val = study.value_of(ansatz.default_initial_params()) # Compute value by simulating time evolution if isinstance(hamiltonian, openfermion.DiagonalCoulombHamiltonian): half_way_hamiltonian = openfermion.DiagonalCoulombHamiltonian( one_body=hamiltonian.one_body, two_body=0.5 * hamiltonian.two_body) elif isinstance(hamiltonian, openfermion.InteractionOperator): half_way_hamiltonian = openfermion.InteractionOperator( constant=hamiltonian.constant, one_body_tensor=hamiltonian.one_body_tensor, two_body_tensor=0.5 * hamiltonian.two_body_tensor) simulation_circuit = cirq.Circuit.from_ops( simulate_trotter(qubits, half_way_hamiltonian, time=ansatz.adiabatic_evolution_time, n_steps=1, order=order, algorithm=trotter_algorithm)) final_state = (preparation_circuit + simulation_circuit).apply_unitary_effect_to_state() correct_val = openfermion.expectation(objective._hamiltonian_linear_op, final_state).real numpy.testing.assert_allclose(obj_val, study_val, atol=atol) numpy.testing.assert_allclose(obj_val, correct_val, atol=atol)
def __init__(self, hamiltonian: openfermion.DiagonalCoulombHamiltonian, iterations: int=1, include_all_cz: bool=False, include_all_z: bool=False, adiabatic_evolution_time: Optional[float]=None, qubits: Optional[Sequence[cirq.QubitId]]=None ) -> None: """ Args: hamiltonian: The Hamiltonian used to generate the ansatz circuit and default initial parameters. iterations: The number of iterations of the basic template to include in the circuit. The number of parameters grows linearly with this value. include_all_cz: Whether to include all possible CZ-type parameterized gates in the ansatz (irrespective of the ansatz Hamiltonian) include_all_z: Whether to include all possible Z-type parameterized gates in the ansatz (irrespective of the ansatz Hamiltonian) adiabatic_evolution_time: The time scale for Hamiltonian evolution used to determine the default initial parameters of the ansatz. This is the value A from the docstring of this class. If not specified, defaults to the sum of the absolute values of the entries of the two-body tensor of the Hamiltonian. qubits: Qubits to be used by the ansatz circuit. If not specified, then qubits will automatically be generated by the `_generate_qubits` method. """ self.hamiltonian = hamiltonian self.iterations = iterations self.include_all_cz = include_all_cz self.include_all_z = include_all_z if adiabatic_evolution_time is None: adiabatic_evolution_time = ( numpy.sum(numpy.abs(hamiltonian.two_body))) self.adiabatic_evolution_time = cast(float, adiabatic_evolution_time) quad_ham = openfermion.QuadraticHamiltonian(hamiltonian.one_body) # Get the coefficients of the one-body terms in the diagonalizing basis self.orbital_energies, _ = quad_ham.orbital_energies() # Get the basis change matrix that diagonalizes the one-body term self.basis_change_matrix = ( quad_ham.diagonalizing_bogoliubov_transform()) super().__init__(qubits)
def test_trotter_ansatzes_evaluate_order_2(ansatz_factory, trotter_algorithm, hamiltonian, atol): """Check that a Trotter ansatz with two iterations and default parameters is consistent with time evolution with two Trotter steps.""" ansatz = ansatz_factory(hamiltonian, iterations=2) qubits = ansatz.qubits preparation_circuit = cirq.Circuit.from_ops( prepare_gaussian_state(qubits, openfermion.QuadraticHamiltonian( hamiltonian.one_body), occupied_orbitals=range(len(qubits) // 2))) study = HamiltonianVariationalStudy( 'study', ansatz, hamiltonian, preparation_circuit=preparation_circuit) simulator = cirq.google.XmonSimulator() # Compute value using ansatz val = study.evaluate(study.default_initial_params()) # Compute value by simulating time evolution quarter_way_hamiltonian = openfermion.DiagonalCoulombHamiltonian( one_body=hamiltonian.one_body, two_body=0.25 * hamiltonian.two_body) three_quarters_way_hamiltonian = openfermion.DiagonalCoulombHamiltonian( one_body=hamiltonian.one_body, two_body=0.75 * hamiltonian.two_body) simulation_circuit = cirq.Circuit.from_ops( simulate_trotter(qubits, quarter_way_hamiltonian, time=0.5 * ansatz.adiabatic_evolution_time, n_steps=1, order=1, algorithm=trotter_algorithm), simulate_trotter(qubits, three_quarters_way_hamiltonian, time=0.5 * ansatz.adiabatic_evolution_time, n_steps=1, order=1, algorithm=trotter_algorithm)) circuit = preparation_circuit + simulation_circuit result = simulator.simulate(circuit) final_state = result.final_state correct_val = openfermion.expectation(study._hamiltonian_linear_op, final_state).real numpy.testing.assert_allclose(val, correct_val, atol=atol)
def __init__(self, hamiltonian: openfermion.InteractionOperator, truncation_threshold: Optional[float]=1e-8, final_rank: Optional[int]=None, spin_basis=True) -> None: self.truncation_threshold = truncation_threshold self.final_rank = final_rank # Perform the low rank decomposition of two-body operator. self.eigenvalues, self.one_body_squares, one_body_correction, _ = ( openfermion.low_rank_two_body_decomposition( hamiltonian.two_body_tensor, truncation_threshold=self.truncation_threshold, final_rank=self.final_rank, spin_basis=spin_basis)) # Get scaled density-density terms and basis transformation matrices. self.scaled_density_density_matrices = [] # type: List[numpy.ndarray] self.basis_change_matrices = [] # type: List[numpy.ndarray] for j in range(len(self.eigenvalues)): density_density_matrix, basis_change_matrix = ( openfermion.prepare_one_body_squared_evolution( self.one_body_squares[j])) self.scaled_density_density_matrices.append( numpy.real(self.eigenvalues[j] * density_density_matrix)) self.basis_change_matrices.append(basis_change_matrix) # Get transformation matrix and orbital energies for one-body terms one_body_coefficients = ( hamiltonian.one_body_tensor + one_body_correction) quad_ham = openfermion.QuadraticHamiltonian(one_body_coefficients) self.one_body_energies, self.one_body_basis_change_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform() ) super().__init__(hamiltonian)
def kevin_lih_cirq(self): x_dim = 3 y_dim = 2 n_sites = x_dim * y_dim n_modes = 2 * n_sites # Create LiH Hamiltonian # ---------------------- bond_length = 1.45 geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., bond_length))] n_active_electrons = 4 n_active_orbitals = 4 lih_hamiltonian = of.load_molecular_hamiltonian( geometry, 'sto-3g', 1, format(bond_length), n_active_electrons, n_active_orbitals) # Generate qubits n_qubits = of.count_qubits(lih_hamiltonian) qubits = cirq.LineQubit.range(n_qubits) # State preparation circuit for eigenstate of one-body term # --------------------------------------------------------- # Set the pseudo-particle orbitals to fill occupied_orbitals = range(n_qubits // 2) # State preparation circuit for eigenstate of one-body term # --------------------------------------------------------- # Set the pseudo-particle orbitals to fill up_orbitals = range(n_sites // 2) down_orbitals = range(n_sites // 2) # Create the circuit lih_state_preparation_circuit = cirq.Circuit.from_ops( ofc.prepare_gaussian_state( qubits, of.QuadraticHamiltonian(lih_hamiltonian.one_body_tensor), occupied_orbitals=(up_orbitals, down_orbitals)), strategy=cirq.InsertStrategy.EARLIEST) # Trotter simulation circuit # -------------------------- n_steps = 10 order = 0 lih_simulation_circuit = cirq.Circuit.from_ops( ofc.simulate_trotter(qubits, lih_hamiltonian, time=1.0, n_steps=n_steps, order=order, algorithm=ofc.trotter.LOW_RANK), strategy=cirq.InsertStrategy.EARLIEST) t0 = time.time() self.kevin_optimize_circuit(lih_state_preparation_circuit) self.kevin_optimize_circuit(lih_simulation_circuit) t1 = time.time() # print('Optimizing circuits took {} seconds'.format(t1 - t0)) # print(lih_state_preparation_circuit.to_text_diagram(transpose=True)) return lih_state_preparation_circuit, lih_simulation_circuit
objective = openfermioncirq.HamiltonianObjective(hamiltonian) # Create a swap network Trotter ansatz. iterations = 1 # This is the number of Trotter steps to use in the ansatz. ansatz = openfermioncirq.SwapNetworkTrotterAnsatz(hamiltonian, iterations=iterations) print('Created a variational ansatz with the following circuit:') print(ansatz.circuit.to_text_diagram(transpose=True)) # Use preparation circuit for mean-field state import cirq preparation_circuit = cirq.Circuit( openfermioncirq.prepare_gaussian_state( ansatz.qubits, openfermion.QuadraticHamiltonian(hamiltonian.one_body), occupied_orbitals=range(n_electrons))) kc_simulator = cirq.KnowledgeCompilationSimulator(preparation_circuit + ansatz.circuit, initial_state=0) # Create a Hamiltonian variational study study = openfermioncirq.VariationalStudy( 'jellium_study', ansatz, objective, preparation_circuit=preparation_circuit) print("Created a variational study with {} qubits and {} parameters".format( len(study.ansatz.qubits), study.num_params))
def get_qubit_hamiltonian(g, basis, charge=0, spin=1, qubit_transf='jw'): ## Create OpenFermion molecule #mol = gto.Mole() #mol.atom = g #mol.basis = basis #mol.spin = spin #mol.charge = charge #mol.symmetry = False ##mol.max_memory = 1024 #mol.build() multiplicity = spin + 1 # spin here is 2S ? mol = MolecularData(g, basis, multiplicity, charge) #mol.load() # Convert to PySCF molecule and run SCF print("Running run_pyscf...") print(f"Time: {time()}") print("=" * 20) mol = run_pyscf(mol) # Freeze some orbitals? occupied_indices = None active_indices = None #import pdb; pdb.set_trace() # Try: occupied_indices = range(183) active_indices = range(15) # when it stops lagging # Get Hamiltonian print("Running get_molecular_hamiltonian...") print(f"Time: {time()}") print("=" * 20) ham = mol.get_molecular_hamiltonian(occupied_indices=occupied_indices, active_indices=active_indices) print("Running get_fermion_operator...") print(f"Time: {time()}") print("=" * 20) hamf = get_fermion_operator(ham) print(f"Running {qubit_transf}...") print(f"Time: {time()}") print("=" * 20) if qubit_transf == 'bk': hamq = bravyi_kitaev(hamf) elif qubit_transf == 'jw': hamq = jordan_wigner(hamf) else: raise (ValueError(qubit_transf, 'Unknown transformation specified')) # Adapted from 10.5281/zenodo.2880550 hamd = openfermion.get_diagonal_coulomb_hamiltonian( hamf, ignore_incompatible_terms=True) nqubits = openfermion.count_qubits(hamd) ansatz = openfermioncirq.SwapNetworkTrotterAnsatz(hamd, iterations=3) print(ansatz.circuit.to_text_diagram(transpose=True)) nelectrons = 8 # TODO: CHECK WHICH ORBITALS ARE SAMPLED!! prep_circuit = cirq.Circuit( openfermioncirq.prepare_gaussian_state( ansatz.qubits, openfermion.QuadraticHamiltonian( hamd.one_body))) # TODO: Verify this line objective = openfermioncirq.HamiltonianObjective(hamd) study = openfermioncirq.VariationalStudy(name='hamil', ansatz=ansatz, objective=objective, preparation_circuit=prep_circuit) print("The energy of the default initial guess is {}.".format( study.value_of(ansatz.default_initial_params()))) print() alg = openfermioncirq.optimization.ScipyOptimizationAlgorithm( kwargs={'method': 'COBYLA'}, uses_bounds=False) opt_params = openfermioncirq.optimization.OptimizationParams( algorith=alg, initial_guess=ansat.default_initial_params()) result = study.optimize(opt_param) print("Optimized energy: {}".format(result.optimal_value)) print()
def __init__(self, hamiltonian: openfermion.InteractionOperator, iterations: int=1, final_rank: Optional[int]=None, include_all_cz: bool=False, include_all_z: bool=False, adiabatic_evolution_time: Optional[float]=None, spin_basis: bool=True, qubits: Optional[Sequence[cirq.Qid]]=None ) -> None: """ Args: hamiltonian: The Hamiltonian used to generate the ansatz circuit and default initial parameters. iterations: The number of iterations of the basic template to include in the circuit. The number of parameters grows linearly with this value. final_rank: The rank at which to truncate the decomposition. include_all_cz: Whether to include all possible CZ-type parameterized gates in the ansatz (irrespective of the ansatz Hamiltonian) include_all_z: Whether to include all possible Z-type parameterized gates in the ansatz (irrespective of the ansatz Hamiltonian) adiabatic_evolution_time: The time scale for Hamiltonian evolution used to determine the default initial parameters of the ansatz. This is the value A from the docstring of this class. If not specified, defaults to the sum of the absolute values of the entries of the two-body tensor of the Hamiltonian. spin_basis: Whether the Hamiltonian is given in the spin orbital (rather than spatial orbital) basis. qubits: Qubits to be used by the ansatz circuit. If not specified, then qubits will automatically be generated by the `_generate_qubits` method. """ self.hamiltonian = hamiltonian self.iterations = iterations self.final_rank = final_rank self.include_all_cz = include_all_cz self.include_all_z = include_all_z if adiabatic_evolution_time is None: adiabatic_evolution_time = ( numpy.sum(numpy.abs(hamiltonian.two_body_tensor))) self.adiabatic_evolution_time = cast(float, adiabatic_evolution_time) # Perform the low rank decomposition of two-body operator. self.eigenvalues, one_body_squares, self.one_body_correction, _ = ( openfermion.low_rank_two_body_decomposition( hamiltonian.two_body_tensor, final_rank=self.final_rank, spin_basis=spin_basis)) # Get scaled density-density terms and basis transformation matrices. self.scaled_density_density_matrices = [] # type: List[numpy.ndarray] self.basis_change_matrices = [] # type: List[numpy.ndarray] for j in range(len(self.eigenvalues)): density_density_matrix, basis_change_matrix = ( openfermion.prepare_one_body_squared_evolution( one_body_squares[j])) self.scaled_density_density_matrices.append( numpy.real(self.eigenvalues[j] * density_density_matrix)) self.basis_change_matrices.append(basis_change_matrix) # Get transformation matrix and orbital energies for one-body terms one_body_coefficients = ( hamiltonian.one_body_tensor + self.one_body_correction) quad_ham = openfermion.QuadraticHamiltonian(one_body_coefficients) self.one_body_energies, self.one_body_basis_change_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform() ) super().__init__(qubits)
def default_initial_params(self) -> numpy.ndarray: """Approximate evolution by H(t) = T + (t/A)V. Sets the parameters so that the ansatz circuit consists of a sequence of second-order Trotter steps approximating the dynamics of the time-dependent Hamiltonian H(t) = T + (t/A)V, where T is the one-body term and V is the two-body term of the Hamiltonian used to generate the ansatz circuit, and t ranges from 0 to A, where A is equal to `self.adibatic_evolution_time`. The number of Trotter steps is equal to the number of iterations in the ansatz. This choice is motivated by the idea of state preparation via adiabatic evolution. The dynamics of H(t) are approximated as follows. First, the total evolution time of A is split into segments of length A / r, where r is the number of Trotter steps. Then, each Trotter step simulates H(t) for a time length of A / r, where t is the midpoint of the corresponding time segment. As an example, suppose A is 100 and the ansatz has two iterations. Then the approximation is achieved with two Trotter steps. The first Trotter step simulates H(25) for a time length of 50, and the second Trotter step simulates H(75) for a time length of 50. """ total_time = self.adiabatic_evolution_time step_time = total_time / self.iterations params = [] for param in self.params(): i = param.subscripts[-1] # Use the midpoint of the time segment interpolation_progress = 0.5 * (2 * i + 1) / self.iterations # One-body term if param.letter == 'U' and len(param.subscripts) == 2: p, _ = param.subscripts one_body_coefficients = ( self.hamiltonian.one_body_tensor + interpolation_progress * self.one_body_correction) quad_ham = openfermion.QuadraticHamiltonian( one_body_coefficients) one_body_energies, _, _ = ( quad_ham.diagonalizing_bogoliubov_transform()) params.append(_canonicalize_exponent( -one_body_energies[p] * step_time / numpy.pi, 2)) # Off-diagonal one-body term elif param.letter == 'V': p, q, j, _ = param.subscripts two_body_coefficients = ( self.scaled_density_density_matrices[j]) params.append(_canonicalize_exponent( -2 * two_body_coefficients[p, q] * interpolation_progress * step_time / numpy.pi, 2)) # Diagonal two-body terms elif param.letter == 'U' and len(param.subscripts) == 3: p, j, _ = param.subscripts two_body_coefficients = ( self.scaled_density_density_matrices[j]) params.append(_canonicalize_exponent( -two_body_coefficients[p, p] * interpolation_progress * step_time / numpy.pi, 2)) return numpy.array(params)
def test_trotter_ansatzes_default_initial_params_iterations_2( ansatz, trotter_algorithm, order, hamiltonian, atol): """Check that a Trotter ansatz with two iterations and default parameters is consistent with time evolution with two Trotter steps.""" objective = HamiltonianObjective(hamiltonian) qubits = ansatz.qubits if isinstance(hamiltonian, openfermion.DiagonalCoulombHamiltonian): one_body = hamiltonian.one_body elif isinstance(hamiltonian, openfermion.InteractionOperator): one_body = hamiltonian.one_body_tensor if isinstance(ansatz, SwapNetworkTrotterHubbardAnsatz): occupied_orbitals = (range(len(qubits) // 4), range(len(qubits) // 4)) else: occupied_orbitals = range(len(qubits) // 2) preparation_circuit = cirq.Circuit( prepare_gaussian_state(qubits, openfermion.QuadraticHamiltonian(one_body), occupied_orbitals=occupied_orbitals)) # Compute value using ansatz circuit and objective circuit = cirq.resolve_parameters( preparation_circuit + ansatz.circuit, ansatz.param_resolver(ansatz.default_initial_params())) result = circuit.final_wavefunction( qubit_order=ansatz.qubit_permutation(qubits)) obj_val = objective.value(result) # Compute value using study study = VariationalStudy('study', ansatz, objective, preparation_circuit=preparation_circuit) study_val = study.value_of(ansatz.default_initial_params()) # Compute value by simulating time evolution if isinstance(hamiltonian, openfermion.DiagonalCoulombHamiltonian): quarter_way_hamiltonian = openfermion.DiagonalCoulombHamiltonian( one_body=hamiltonian.one_body, two_body=0.25 * hamiltonian.two_body) three_quarters_way_hamiltonian = openfermion.DiagonalCoulombHamiltonian( one_body=hamiltonian.one_body, two_body=0.75 * hamiltonian.two_body) elif isinstance(hamiltonian, openfermion.InteractionOperator): quarter_way_hamiltonian = openfermion.InteractionOperator( constant=hamiltonian.constant, one_body_tensor=hamiltonian.one_body_tensor, two_body_tensor=0.25 * hamiltonian.two_body_tensor) three_quarters_way_hamiltonian = openfermion.InteractionOperator( constant=hamiltonian.constant, one_body_tensor=hamiltonian.one_body_tensor, two_body_tensor=0.75 * hamiltonian.two_body_tensor) simulation_circuit = cirq.Circuit( simulate_trotter(qubits, quarter_way_hamiltonian, time=0.5 * ansatz.adiabatic_evolution_time, n_steps=1, order=order, algorithm=trotter_algorithm), simulate_trotter(qubits, three_quarters_way_hamiltonian, time=0.5 * ansatz.adiabatic_evolution_time, n_steps=1, order=order, algorithm=trotter_algorithm)) final_state = (preparation_circuit + simulation_circuit).final_wavefunction() correct_val = openfermion.expectation(objective._hamiltonian_linear_op, final_state).real numpy.testing.assert_allclose(obj_val, study_val, atol=atol) numpy.testing.assert_allclose(obj_val, correct_val, atol=atol)