def __init__(self, orbprop, orb_qubit_map=None, symm_eigval_map=None): ham_fop = construct_exact_ham(orbprop) ham_qop = openfermion.jordan_wigner(ham_fop) n_site = openfermion.count_qubits(ham_qop) if symm_eigval_map is not None: symm_paulistr = symm.symmetry_pauli_string( orbprop, operation_list=list(symm_eigval_map)) for symmop, qop in symm_paulistr.items(): print('[ham_qop, {:5s}] = [ham_qop, {}] = {}'.format( symmop, str(qop), openfermion.commutator(ham_qop, qop))) self.remover = symm.SymmRemover(n_site, list(symm_paulistr.values())) self.remover.set_eigvals( [symm_eigval_map[symm] for symm in symm_paulistr]) ham_qop_tapered = self.remover.remove_symm_qubits(ham_qop) print(self.remover) print('len(ham ) = ', len(ham_qop.terms)) print('len(ham[tapered]) = ', len(ham_qop_tapered.terms)) ham_qop = ham_qop_tapered self.ham_fop = ham_fop self.ham_qop = ham_qop self.ham_tensor = openfermion.get_sparse_operator(ham_qop) self.n_site = n_site self.n_qubit = openfermion.count_qubits(ham_qop) nterm = [0] * ( n_site + 1 ) # number of paulistrs which length is n (n=0,1,..,n_site). for k in ham_qop.terms: nterm[len(k)] += 1 self.nterm = nterm
def reverse_qubit_order(qubit_operator: QubitOperator, n_qubits: Optional[int] = None): """Reverse the order of qubit indices in a qubit operator. Args: qubit_operator (openfermion.QubitOperator): the operator n_qubits (int): total number of qubits. Needs to be provided when the size of the system of interest is greater than the size of qubit operator (optional) Returns: reversed_op (openfermion.ops.QubitOperator) """ reversed_op = QubitOperator() if n_qubits is None: n_qubits = count_qubits(qubit_operator) if n_qubits < count_qubits(qubit_operator): raise ValueError("Invalid number of qubits specified.") for term in qubit_operator.terms: new_term = [] for factor in term: new_factor = list(factor) new_factor[0] = n_qubits - 1 - new_factor[0] new_term.append(tuple(new_factor)) reversed_op += QubitOperator(tuple(new_term), qubit_operator.terms[term]) return reversed_op
def test_load_molecular_hamiltonian(): geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.4))] lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian( geometry, 'sto-3g', 1, 0, 2, 2) assert openfermion.count_qubits(lih_hamiltonian) == 4 lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian( geometry, 'sto-3g', 1, 0, 2, 3) assert openfermion.count_qubits(lih_hamiltonian) == 6 lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian( geometry, 'sto-3g', 1, 0, None, None) assert openfermion.count_qubits(lih_hamiltonian) == 12
def produce_simulation_test_parameters( hamiltonian: Hamiltonian, time: float, seed: Optional[int] = None) -> Tuple[numpy.ndarray, numpy.ndarray]: """Produce objects for testing Hamiltonian simulation. Produces a random initial state and evolves it under the given Hamiltonian for the specified amount of time. Returns the initial state and final state. Args: hamiltonian: The Hamiltonian to evolve under. time: The time to evolve for seed: An RNG seed. """ n_qubits = openfermion.count_qubits(hamiltonian) # Construct a random initial state initial_state = openfermion.haar_random_vector(2**n_qubits, seed) # Simulate exact evolution hamiltonian_sparse = openfermion.get_sparse_operator(hamiltonian) exact_state = scipy.sparse.linalg.expm_multiply( -1j * time * hamiltonian_sparse, initial_state) # Make sure the time is not too small assert fidelity(exact_state, initial_state) < .95 return initial_state, exact_state
def qubitop_to_paulisum( qubit_operator: QubitOperator, qubits: Union[List[cirq.GridQubit], List[cirq.LineQubit]] = None, ) -> cirq.PauliSum: """Convert and openfermion QubitOperator to a cirq PauliSum Args: qubit_operator (openfermion.QubitOperator): The openfermion operator to convert qubits() Returns: cirq.PauliSum """ operator_map = {"X": cirq.X, "Y": cirq.Y, "Z": cirq.Z} if qubits is None: qubits = [cirq.GridQubit(i, 0) for i in range(count_qubits(qubit_operator))] converted_sum = cirq.PauliSum() for term, coefficient in qubit_operator.terms.items(): # Identity term if len(term) == 0: converted_sum += coefficient continue cirq_term = cirq.PauliString() for qubit_index, operator in term: cirq_term *= operator_map[operator](qubits[qubit_index]) converted_sum += cirq_term * coefficient return converted_sum
def _get_diagonal_component_polynomial_tensor(polynomial_tensor): """Get the component of an interaction operator that is diagonal in the computational basis under Jordan-Wigner transformation (i.e., the terms that can be expressed as products of number operators). Args: interaction_operator (openfermion.ops.InteractionOperator): the operator Returns: tuple: two openfermion.ops.InteractionOperator objects. The first is the diagonal component, and the second is the remainder. """ n_modes = count_qubits(polynomial_tensor) remainder_tensors = {} diagonal_tensors = {} diagonal_tensors[()] = polynomial_tensor.constant for key in polynomial_tensor.n_body_tensors: if key == (): continue remainder_tensors[key] = np.zeros((n_modes,) * len(key), complex) diagonal_tensors[key] = np.zeros((n_modes,) * len(key), complex) for indices in itertools.product(range(n_modes), repeat=len(key)): creation_counts = {} annihilation_counts = {} for meta_index, index in enumerate(indices): if key[meta_index] == 0: if annihilation_counts.get(index) is None: annihilation_counts[index] = 1 else: annihilation_counts[index] += 1 elif key[meta_index] == 1: if creation_counts.get(index) is None: creation_counts[index] = 1 else: creation_counts[index] += 1 term_is_diagonal = True for index in creation_counts: if creation_counts[index] != annihilation_counts.get(index): term_is_diagonal = False break if term_is_diagonal: for index in annihilation_counts: if annihilation_counts[index] != creation_counts.get(index): term_is_diagonal = False break if term_is_diagonal: diagonal_tensors[key][indices] = polynomial_tensor.n_body_tensors[key][ indices ] else: remainder_tensors[key][indices] = polynomial_tensor.n_body_tensors[key][ indices ] return PolynomialTensor(diagonal_tensors), PolynomialTensor(remainder_tensors)
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator: if openfermion.count_qubits(fermion_operator) != self.n_orbitals * 2: raise Exception( "TaperedBravyiKitaev not ready for UCC generators yet") return openfermion.symmetry_conserving_bravyi_kitaev( fermion_operator, active_orbitals=self.active_orbitals, active_fermions=self.active_fermions)
def do_transform(self, fermion_operator: openfermion.FermionOperator, *args, **kwargs) -> openfermion.QubitOperator: n_qubits = openfermion.count_qubits(fermion_operator) if n_qubits != self.n_orbitals * 2: raise Exception( "BravyiKitaevFast transformation currently only possible for full Hamiltonians (no UCC generators).\nfermion_operator was {}" .format(fermion_operator)) op = openfermion.get_interaction_operator(fermion_operator) return openfermion.bravyi_kitaev_fast(op)
def block_reduce(qubit_operator: openfermion.QubitOperator): """ Reduce the qubit operator into blocks. If a qubit in `qubit_operator` is only acted by the identity operator or one kind of Pauli operator (or both of them), it is considered to be "generally stationary", and can be used to reduce the operator into several blocks. See also: `reduce` `inactivate_stationary_qubits` :param qubit_operator: The qubit operator to be block-reduced. :return: The reduced blocks. Example: >>> op = (openfermion.QubitOperator("X0 Y1 Z3 ", 0.1) + ... openfermion.QubitOperator("Y0 Z1 Z3 Y4", 0.2) + ... openfermion.QubitOperator("X0 Z1 ", 0.3) + ... openfermion.QubitOperator("Y0 X4", 0.4)) We can find that qubit 3 is "generally stationary", and thus we can use it to do the block reduction: >>> block_reduce(op) {((2, 'I'), (3, 'Z')): 0.1 [X0 Y1] + 0.2 [Y0 Z1 Y2], ((2, 'I'), (3, 'I')): 0.3 [X0 Z1] + 0.4 [Y0 X2]} Here, `0.1 [X0 Y1] + 0.2 [Y0 Z1]` and `0.3 [X0 Z1] + 0.4 [Y0]` are the two blocks corresponding to "I2 Z3" and "I2 I3". """ n_qubits = openfermion.count_qubits(qubit_operator) appeared_paulis = [set() for _ in range(n_qubits)] for term, _ in qubit_operator.terms.items(): for qubit_index, pauli in term: appeared_paulis[qubit_index].add(pauli) reducible_qubits = [] for i, paulis in enumerate(appeared_paulis): if len(paulis) <= 1: reducible_qubits.append(i) grouped = group(qubit_operator, by_qubit_indices=reducible_qubits) reduced = {} for paulis_at_reducible_qubits, op in grouped.items(): reduced[paulis_at_reducible_qubits] = reduce_inactive_qubits( op, reducible_qubits)[0] return reduced
def get_polynomial_tensor(fermion_operator, n_qubits=None): r"""Convert a fermionic operator to a Polynomial Tensor. Args: fermion_operator (openferion.ops.FermionOperator): The operator. n_qubits (int): The number of qubits to be included in the PolynomialTensor. Must be at least equal to the number of qubits that are acted on by fermion_operator. If None, then the number of qubits is inferred from fermion_operator. Returns: openfermion.ops.PolynomialTensor: The tensor representation of the operator. """ if not isinstance(fermion_operator, FermionOperator): raise TypeError("Input must be a FermionOperator.") if n_qubits is None: n_qubits = count_qubits(fermion_operator) if n_qubits < count_qubits(fermion_operator): raise ValueError("Invalid number of qubits specified.") # Normal order the terms and initialize. fermion_operator = normal_ordered(fermion_operator) tensor_dict = {} # Loop through terms and assign to matrix. for term in fermion_operator.terms: coefficient = fermion_operator.terms[term] # Handle constant shift. if len(term) == 0: tensor_dict[()] = coefficient else: key = tuple([operator[1] for operator in term]) if tensor_dict.get(key) is None: tensor_dict[key] = np.zeros((n_qubits,) * len(key), complex) indices = tuple([operator[0] for operator in term]) tensor_dict[key][indices] = coefficient return PolynomialTensor(tensor_dict)
def get_ground_state_rdm_from_qubit_op( qubit_operator: QubitOperator, n_particles: int ) -> InteractionRDM: """Diagonalize operator and compute the ground state 1- and 2-RDM Args: qubit_operator (openfermion.QubitOperator): The openfermion operator to diagonalize n_particles (int): number of particles in the target ground state Returns: rdm (openfermion.InteractionRDM): interaction RDM of the ground state with the particle number n_particles """ sparse_operator = get_sparse_operator(qubit_operator) e, ground_state_wf = jw_get_ground_state_at_particle_number( sparse_operator, n_particles ) # float/np.array pair n_qubits = count_qubits(qubit_operator) one_body_tensor = [] for i in range(n_qubits): for j in range(n_qubits): idag_j = get_sparse_operator( FermionOperator(f"{i}^ {j}"), n_qubits=n_qubits ) idag_j = idag_j.toarray() one_body_tensor.append( np.conjugate(ground_state_wf) @ idag_j @ ground_state_wf ) one_body_tensor = np.array(one_body_tensor) one_body_tensor = one_body_tensor.reshape(n_qubits, n_qubits) two_body_tensor = np.zeros((n_qubits,) * 4, dtype=complex) for p in range(n_qubits): for q in range(0, p + 1): for r in range(n_qubits): for s in range(0, r + 1): pdag_qdag_r_s = get_sparse_operator( FermionOperator(f"{p}^ {q}^ {r} {s}"), n_qubits=n_qubits ) pdag_qdag_r_s = pdag_qdag_r_s.toarray() rdm_element = ( np.conjugate(ground_state_wf) @ pdag_qdag_r_s @ ground_state_wf ) two_body_tensor[p, q, r, s] = rdm_element two_body_tensor[q, p, r, s] = -rdm_element two_body_tensor[q, p, s, r] = rdm_element two_body_tensor[p, q, s, r] = -rdm_element return InteractionRDM(one_body_tensor, two_body_tensor)
def test_expectation_values_paulisum(qubitop, state_binary): """Test PauliSum and QubitOperator expectation value.""" n_qubits = openfermion.count_qubits(qubitop) state = numpy.zeros(2**n_qubits, dtype='complex64') state[int(state_binary, 2)] = 1.0 qubit_map = {cirq.LineQubit(i): i for i in range(n_qubits)} pauli_str = qubit_operator_to_pauli_sum(qubitop, list(qubit_map.keys())) op_mat = openfermion.get_sparse_operator(qubitop, n_qubits) expct_qop = openfermion.expectation(op_mat, state) expct_pauli = pauli_str.expectation_from_state_vector(state, qubit_map) numpy.testing.assert_allclose(expct_qop, expct_pauli)
def test_simulate_trotter_simulate(hamiltonian, time, initial_state, exact_state, order, n_steps, algorithm, result_fidelity): n_qubits = openfermion.count_qubits(hamiltonian) qubits = cirq.LineQubit.range(n_qubits) start_state = initial_state circuit = cirq.Circuit( simulate_trotter(qubits, hamiltonian, time, n_steps, order, algorithm)) final_state = circuit.apply_unitary_effect_to_state(start_state) correct_state = exact_state assert fidelity(final_state, correct_state) > result_fidelity # Make sure the time wasn't too small assert fidelity(final_state, start_state) < 0.95 * result_fidelity
def test_cirq_to_fqe_single(self): """Wrap together all the routines to build a wavefuntion that can be read by the fqe by simply passing a cirq wavefunction. """ cof = numpy.array([0.3901112 - 0.1543j, 0.01213 + 0.79120j], dtype=numpy.complex128) cof /= numpy.sqrt(numpy.vdot(cof, cof)) wfn_ops = cof[0] * (ladder_op(0, 1) * ladder_op(1, 1) * ladder_op(2, 1)) wfn_ops += cof[1] * (ladder_op(0, 1) * ladder_op(2, 1) * ladder_op(3, 1)) qpu = LineQubit.range(count_qubits(wfn_ops)) cirq_wfn = qubit_wavefunction_from_vacuum(wfn_ops, qpu) fqe_wfn = transform.cirq_to_fqe_single(cirq_wfn, 3, 1, None) fqe_jw = jordan_wigner(fqe_wfn) test_key = list(wfn_ops.terms.keys()) for keyval in list(fqe_jw.terms.keys()): self.assertTrue(keyval in test_key) for i in fqe_jw.terms: self.assertAlmostEqual(fqe_jw.terms[i], wfn_ops.terms[i])
def test_simulate_trotter_simulate_controlled( hamiltonian, time, initial_state, exact_state, order, n_steps, algorithm, result_fidelity): n_qubits = openfermion.count_qubits(hamiltonian) qubits = cirq.LineQubit.range(n_qubits) control = cirq.LineQubit(-1) zero = [1, 0] one = [0, 1] start_state = (numpy.kron(zero, initial_state) + numpy.kron(one, initial_state)) / numpy.sqrt(2) circuit = cirq.Circuit.from_ops(simulate_trotter( qubits, hamiltonian, time, n_steps, order, algorithm, control)) final_state = circuit.apply_unitary_effect_to_state(start_state) correct_state = (numpy.kron(zero, initial_state) + numpy.kron(one, exact_state)) / numpy.sqrt(2) assert fidelity(final_state, correct_state) > result_fidelity # Make sure the time wasn't too small assert fidelity(final_state, start_state) < 0.95 * result_fidelity
def test_low_rank_trotter_ansatz_params(): n = openfermion.count_qubits(lih_hamiltonian) final_rank = 2 ansatz = LowRankTrotterAnsatz(lih_hamiltonian, final_rank=final_rank, include_all_cz=True, include_all_z=True) assert len(list(ansatz.params())) == n + final_rank * (n + n * (n - 1) // 2) ansatz = LowRankTrotterAnsatz(lih_hamiltonian, final_rank=2) assert set(ansatz.params()) == { cirq.Symbol(name) for name in { 'U_0_0', 'U_0_0_0', 'U_0_1_0', 'U_1_0', 'U_1_0_0', 'U_1_1_0', 'U_2_0', 'U_2_0_0', 'U_2_1_0', 'U_3_0', 'U_3_0_0', 'U_3_1_0', 'V_0_1_0_0', 'V_0_1_1_0', 'V_0_2_0_0', 'V_0_2_1_0', 'V_0_3_0_0', 'V_0_3_1_0', 'V_1_2_0_0', 'V_1_2_1_0', 'V_1_3_0_0', 'V_1_3_1_0', 'V_2_3_0_0', 'V_2_3_1_0' } }
def qubitop_to_qiskitpauli( qubit_operator: QubitOperator) -> WeightedPauliOperator: """Convert a OpenFermion QubitOperator to a WeightedPauliOperator. Args: qubit_operator: OpenFermion QubitOperator to convert Returns: WeightedPauliOperator representing the qubit operator """ if not isinstance(qubit_operator, QubitOperator): raise TypeError( "qubit_operator must be an OpenFermion QubitOperator object") terms = [] for qubit_terms, coefficient in qubit_operator.terms.items(): string_term = "I" * count_qubits(qubit_operator) for i, (term_qubit, term_pauli) in enumerate(qubit_terms): string_term = (string_term[:term_qubit] + term_pauli + string_term[term_qubit + 1:]) terms.append([coefficient, Pauli.from_label(string_term)]) return WeightedPauliOperator(terms)
def bit_mask_of_modes_acted_on_by_fermionic_terms(fermion_term_list, n_qubits=None): """Create a mask of which modes of the system are acted on by which terms. Args: fermion_term_list (list of FermionOperators): A list of fermionic terms to calculate the bitmask for. n_qubits (int): The number of qubits (modes) in the system. If not specified, defaults to the maximum of any term in fermion_term_list. Returns: An n_qubits x len(fermion_term_list) boolean numpy array of whether each term acts on the given mode index. Raises: ValueError: if n_qubits is too small for the given terms. """ if n_qubits is None: n_qubits = 0 for term in fermion_term_list: n_qubits = max(n_qubits, count_qubits(term)) mask = numpy.zeros((n_qubits, len(fermion_term_list)), dtype=bool) for term_number, term in enumerate(fermion_term_list): actions = term.terms for action in actions: for single_operator in action: mode = single_operator[0] try: mask[mode][term_number] = True except IndexError: raise ValueError('Bad n_qubits: must be greater than ' 'highest mode in any FermionOperator.') return mask
def test_jellium_hamiltonian_correctly_broken_up(self): grid = Grid(2, 3, 1.) hamiltonian = jellium_model(grid, spinless=True, plane_wave=False) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays( hamiltonian)) potential = sum(potential_terms, FermionOperator.zero()) kinetic = sum(kinetic_terms, FermionOperator.zero()) true_potential = dual_basis_jellium_model(grid, spinless=True, kinetic=False) true_kinetic = dual_basis_jellium_model(grid, spinless=True, potential=False) for i in range(count_qubits(true_kinetic)): coeff = true_kinetic.terms.get(((i, 1), (i, 0))) if coeff: true_kinetic -= FermionOperator(((i, 1), (i, 0)), coeff) true_potential += FermionOperator(((i, 1), (i, 0)), coeff) self.assertEqual(potential, true_potential) self.assertEqual(kinetic, true_kinetic)
def reduce_inactive_qubits( qubit_operator: openfermion.QubitOperator, inactive_qubits: typing.Optional[typing.Iterable[int]] = None ) -> typing.Tuple[openfermion.QubitOperator, int]: """ Reduce the inactive qubits in a qubit operator. :param qubit_operator: The qubit operator to be simplified (i.e. reduced). :param inactive_qubits: The qubits to be reduced from the qubit operator. If given out explicitly, the qubits in `inactive_qubits` will all be removed from the qubit operator (no matter whether it is really "inactive"). If left to be default, the inactive qubits to be removed will be automatically determined. Here, "inactive" means that there are not any effective Pauli operators ("X", "Y", "Z") acting on the qubits in `qubit_operator`. :return: The reduced qubit operator, and the number of reduced qubits. Example: >>> op = (openfermion.QubitOperator("X0 Y1", 0.1) + ... openfermion.QubitOperator("Z0 Y1 X3", 0.2)) >>> reduce_inactive_qubits(op) (0.1 [X0 Y1] + 0.2 [Z0 Y1 X2], 1) >>> reduce_inactive_qubits(op, (0, 2)) (0.1 [Y0] + 0.2 [Y0 X1], 2) >>> reduce_inactive_qubits(op, (0,)) (0.1 [Y0] + 0.2 [Y0 X2], 1) """ if inactive_qubits is None: active_qubits = set() for term in qubit_operator.terms.keys(): for qubit_index, pauli in term: active_qubits.add(qubit_index) active_qubits = sorted(active_qubits) else: active_qubits = sorted( q for q in range(0, openfermion.count_qubits(qubit_operator)) if q not in inactive_qubits) reduced_qubit_op = openfermion.QubitOperator() for term, coefficient in qubit_operator.terms.items(): reduced_term = tuple((active_qubits.index(qubit_index), pauli) for qubit_index, pauli in term if qubit_index in active_qubits) _op = openfermion.QubitOperator() _op.terms[reduced_term] = coefficient reduced_qubit_op += _op if inactive_qubits is None: # if in the default case: assert len(active_qubits) == openfermion.count_qubits(reduced_qubit_op) else: # For cases like: # # reduce_inactive_qubits( # openfermion.QubitOperator("Z0 Y1 X3", 0.2), # (3,) # ) # # active_qubits == (0, 1, 2), but the reduced operator is # # openfermion.QubitOperator("Z0 Y1", 0.2) # # counting whose qubits will give out 2 instead of 3. pass num_reduced_qubits = \ openfermion.count_qubits(qubit_operator) - len(active_qubits) return reduced_qubit_op, num_reduced_qubits
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 _generate_qubits(self) -> Sequence[cirq.QubitId]: return cirq.LineQubit.range(openfermion.count_qubits(self.hamiltonian))
def _generate_qubits(self) -> Sequence[cirq.QubitId]: """Produce qubits that can be used by the ansatz circuit.""" return cirq.LineQubit.range(openfermion.count_qubits(self.hamiltonian))
def fermionic_swap_trotter_error_operator_diagonal_two_body(hamiltonian): """Compute the fermionic swap network Trotter error of a diagonal two-body Hamiltonian. Args: hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to compute the Trotter error for. Returns: error_operator: The second-order Trotter error operator. Notes: Follows Eq 9 of Poulin et al., arXiv:1406.4920, applied to the Trotter step detailed in Kivlichan et al., arxiv:1711.04789. """ single_terms = numpy.array( simulation_ordered_grouped_low_depth_terms_with_info(hamiltonian)[0]) # Cache the halved terms for use in the second commutator. halved_single_terms = single_terms / 2.0 term_mode_mask = bit_mask_of_modes_acted_on_by_fermionic_terms( single_terms, count_qubits(hamiltonian)) error_operator = FermionOperator.zero() for beta, term_beta in enumerate(single_terms): modes_acted_on_by_term_beta = set() for beta_action in term_beta.terms: modes_acted_on_by_term_beta.update( set(operator[0] for operator in beta_action)) beta_mode_mask = numpy.logical_or.reduce( [term_mode_mask[mode] for mode in modes_acted_on_by_term_beta]) # alpha_prime indices that could have a nonzero commutator, i.e. # there's overlap between the modes the corresponding terms act on. valid_alpha_primes = numpy.where(beta_mode_mask)[0] # Only alpha_prime < beta enters the error operator; filter for this. valid_alpha_primes = valid_alpha_primes[valid_alpha_primes < beta] for alpha_prime in valid_alpha_primes: term_alpha_prime = single_terms[alpha_prime] inner_commutator_term = ( commutator_ordered_diagonal_coulomb_with_two_body_operator( term_beta, term_alpha_prime)) modes_acted_on_by_inner_commutator = set() for inner_commutator_action in inner_commutator_term.terms: modes_acted_on_by_inner_commutator.update( set(operator[0] for operator in inner_commutator_action)) # If the inner commutator has no action, the commutator is zero. if not modes_acted_on_by_inner_commutator: continue inner_commutator_mask = numpy.logical_or.reduce([ term_mode_mask[mode] for mode in modes_acted_on_by_inner_commutator ]) # alpha indices that could have a nonzero commutator. valid_alphas = numpy.where(inner_commutator_mask)[0] # Filter so alpha <= beta in the double commutator. valid_alphas = valid_alphas[valid_alphas <= beta] for alpha in valid_alphas: # If alpha = beta, only use half the term. if alpha != beta: outer_term_alpha = single_terms[alpha] else: outer_term_alpha = halved_single_terms[alpha] # Add the partial double commutator to the error operator. commutator_ordered_diagonal_coulomb_with_two_body_operator( outer_term_alpha, inner_commutator_term, prior_terms=error_operator) # Divide by 12 to match the error operator definition. error_operator /= 12.0 return error_operator
def _generate_qubits(self) -> Sequence[cirq.Qid]: """Specifies the number of required qubits based on the molecule hamiltonian""" qubit_count = of.count_qubits(self._get_hamiltonian()) row = int(np.ceil(np.sqrt(qubit_count))) return [cirq.GridQubit(i, j) for i in range(row) for j in range(row)]
def estimate_correlation(ham_qop, state, dt, max_trotter_step, outputter, savefilename=None): const_term = ham_qop.terms[()] n_site = openfermion.count_qubits(ham_qop) outputter.n_qubit = n_site outputter.n_trott_step = max_trotter_step #circuit_time_evo = qulacs.QuantumCircuit(n_site) #trotter.trotter_step_2nd_order(circuit_time_evo, -1j * dt * ham_qop) trotterstep = trotter.TrotterStep(n_site, -1j * dt * ham_qop) circuit_time_evo = trotterstep._circuit outputter.ngate = trotterstep.count_gates() print(trotterstep) print(circuit_time_evo) ham_tensor = openfermion.get_sparse_operator(ham_qop) state_vec_exact = convert_state_vector(n_site, state.get_vector()) steps = np.arange(max_trotter_step + 1) save_state_vec_exact = np.empty((len(steps), 2**n_site), np.complex128) save_state_vec_trotter = np.empty((len(steps), 2**n_site), np.complex128) save_time_energy_fidelity = np.empty((len(steps), 4), float) save_time_energy_fidelity[:, 0] = steps * dt time_sim = 0 for istep, n_trotter_step in enumerate(steps): state_vec_trotter = convert_state_vector(n_site, state.get_vector()) # calculate energy and fidelity energy_exact = openfermion.expectation(ham_tensor, state_vec_exact) energy_trotter = openfermion.expectation(ham_tensor, state_vec_trotter) fidelity = np.abs( np.dot(np.conjugate(state_vec_exact), state_vec_trotter))**2 # save save_state_vec_exact[istep] = state_vec_exact save_state_vec_trotter[istep] = state_vec_trotter save_time_energy_fidelity[istep, 1] = energy_exact.real save_time_energy_fidelity[istep, 2] = energy_trotter.real save_time_energy_fidelity[istep, 3] = fidelity # time propergation time_bgn = time() circuit_time_evo.update_quantum_state(state) time_sim += time() - time_bgn state_vec_exact = scipy.sparse.linalg.expm_multiply( -1j * dt * ham_tensor, state_vec_exact) corr_exact = np.dot(save_state_vec_exact[0], save_state_vec_exact.T) corr_trotter = np.dot( save_state_vec_trotter[0], save_state_vec_trotter.T) # * np.exp(-1j * dt * steps * const_term) outputter.time_sim = time_sim if savefilename: np.save(savefilename + '_ham_tensor', ham_tensor.todense()) np.save(savefilename + '_corr_exact.npy', corr_exact) np.save(savefilename + '_corr_trotter.npy', corr_trotter) np.save(savefilename + '_state_vec_exact.npy', save_state_vec_exact) np.save(savefilename + '_state_vec_trotter.npy', save_state_vec_trotter) np.save(savefilename + '_time_energy_fidelity.npy', save_time_energy_fidelity) return EstimateCorrelationResult(corr_exact, corr_trotter, save_state_vec_exact, save_state_vec_trotter, save_time_energy_fidelity)
def estimate_corr_new__(ham, dt, max_trotter_step, outputter, savefilename=None): n_site = openfermion.count_qubits(ham.ham_qop) #state = ham.state #state_vec_exact = convert_state_vector(n_site, state.get_vector()) steps = np.arange(max_trotter_step + 1) save_state_vec_exact = np.empty((len(steps), 2**n_site), np.complex128) save_state_vec_trotter = np.empty((len(steps), 2**n_site), np.complex128) save_time_energy_fidelity = np.empty((len(steps), 4), float) save_time_energy_fidelity[:, 0] = steps * dt time_sim = 0 ham.init_propagate(dt) ham_tensor = openfermion.get_sparse_operator(ham.ham_qop) for istep, n_trotter_step in enumerate(steps): state_vec_trotter = convert_state_vector(n_site, ham.state.get_vector()) # calculate energy and fidelity energy_exact = openfermion.expectation(ham_tensor, state_vec_exact) energy_trotter = openfermion.expectation(ham_tensor, state_vec_trotter) fidelity = np.abs( np.dot(np.conjugate(state_vec_exact), state_vec_trotter))**2 # save save_state_vec_exact[istep] = state_vec_exact save_state_vec_trotter[istep] = state_vec_trotter save_time_energy_fidelity[istep, 1] = energy_exact.real save_time_energy_fidelity[istep, 2] = energy_trotter.real save_time_energy_fidelity[istep, 3] = fidelity # time propergation time_bgn = time() ham.propagate() time_sim += time() - time_bgn state_vec_exact = scipy.sparse.linalg.expm_multiply( -1j * dt * ham_tensor, state_vec_exact) corr_exact = np.dot(save_state_vec_exact[0], save_state_vec_exact.T) corr_trotter = np.dot(save_state_vec_trotter[0], save_state_vec_trotter.T) outputter.n_qubit = n_site outputter.n_trott_step = max_trotter_step outputter.ngate = ham.trotterstep.count_gates() outputter.time_sim = time_sim if savefilename: np.save(savefilename + '_ham_tensor', ham_tensor.todense()) np.save(savefilename + '_corr_exact.npy', corr_exact) np.save(savefilename + '_corr_trotter.npy', corr_trotter) np.save(savefilename + '_state_vec_exact.npy', save_state_vec_exact) np.save(savefilename + '_state_vec_trotter.npy', save_state_vec_trotter) np.save(savefilename + '_time_energy_fidelity.npy', save_time_energy_fidelity) return EstimateCorrelationResult(corr_exact, corr_trotter, save_state_vec_exact, save_state_vec_trotter, save_time_energy_fidelity)
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
def split_operator_trotter_error_operator_diagonal_two_body( hamiltonian, order): """Compute the split-operator Trotter error of a diagonal two-body Hamiltonian. Args: hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to compute the Trotter error for. order (str): Whether to simulate the split-operator Trotter step with the kinetic energy T first (order='T+V') or with the potential energy V first (order='V+T'). Returns: error_operator: The second-order Trotter error operator. Notes: The second-order split-operator Trotter error is calculated from the double commutator [T, [V, T]] + [V, [V, T]] / 2 when T is simulated before V (i.e. exp(-iTt/2) exp(-iVt) exp(-iTt/2)), and from the double commutator [V, [T, V]] + [T, [T, V]] / 2 when V is simulated before T, following Equation 9 of "The Trotter Step Size Required for Accurate Quantum Simulation of Quantum Chemistry" by Poulin et al. The Trotter error operator is then obtained by dividing by 12. """ n_qubits = count_qubits(hamiltonian) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian)) # Cache halved potential and kinetic terms for the second commutator. halved_potential_terms = potential_terms / 2.0 halved_kinetic_terms = kinetic_terms / 2.0 # Assign the outer term of the second commutator based on the ordering. outer_potential_terms = (halved_potential_terms if order == 'T+V' else potential_terms) outer_kinetic_terms = (halved_kinetic_terms if order == 'V+T' else kinetic_terms) potential_mask = bit_mask_of_modes_acted_on_by_fermionic_terms( potential_terms, n_qubits) kinetic_mask = bit_mask_of_modes_acted_on_by_fermionic_terms( kinetic_terms, n_qubits) error_operator = FermionOperator.zero() for potential_term in potential_terms: modes_acted_on_by_potential_term = set() for potential_term_action in potential_term.terms: modes_acted_on_by_potential_term.update( set(operator[0] for operator in potential_term_action)) if not modes_acted_on_by_potential_term: continue potential_term_mode_mask = numpy.logical_or.reduce( [kinetic_mask[mode] for mode in modes_acted_on_by_potential_term]) for kinetic_term in kinetic_terms[potential_term_mode_mask]: inner_commutator_term = ( commutator_ordered_diagonal_coulomb_with_two_body_operator( potential_term, kinetic_term)) modes_acted_on_by_inner_commutator = set() for inner_commutator_action in inner_commutator_term.terms: modes_acted_on_by_inner_commutator.update( set(operator[0] for operator in inner_commutator_action)) if not modes_acted_on_by_inner_commutator: continue inner_commutator_mode_mask = numpy.logical_or.reduce([ potential_mask[mode] for mode in modes_acted_on_by_inner_commutator ]) # halved_potential_terms for T+V order, potential_terms for V+T for outer_potential_term in outer_potential_terms[ inner_commutator_mode_mask]: commutator_ordered_diagonal_coulomb_with_two_body_operator( outer_potential_term, inner_commutator_term, prior_terms=error_operator) inner_commutator_mode_mask = numpy.logical_or.reduce([ kinetic_mask[qubit] for qubit in modes_acted_on_by_inner_commutator ]) # kinetic_terms for T+V order, halved_kinetic_terms for V+T for outer_kinetic_term in outer_kinetic_terms[ inner_commutator_mode_mask]: commutator_ordered_diagonal_coulomb_with_two_body_operator( outer_kinetic_term, inner_commutator_term, prior_terms=error_operator) # Divide by 12 to match the error operator definition. # If order='V+T', also flip the sign to account for inner_commutator_term # not flipping between the different orderings. if order == 'T+V': error_operator /= 12.0 else: error_operator /= -12.0 return error_operator