'soscf':'false', 'scf_type':'pk', 'maxiter':'1e6', 'num_amps_print':'1e6', 'r_convergence':'1e-6', 'd_convergence':'1e-6', 'e_convergence':'1e-6', 'ints_tolerance':'EQUALITY_TOLERANCE', 'damping_percentage':'0' }) energy, wavefunction = psi4.energy('scf', return_wfn=True, molecule=psi4_mol) hamiltonian = get_molecular_hamiltonian(mol, psi4_mol, energy, wavefunction, docc_orbitals, active_orbitals, active_space) qubit_hamiltonian = jordan_wigner(hamiltonian) qubit_hamiltonian.compress() if active_space == False: n_qubits = mol.n_qubits n_electrons = mol.n_electrons else: n_qubits = 2 * n_active_orbitals n_electrons = 2 * n_docc_orbitals # backend = IBMQBackend("ibmqx4") backend = AerBackend() packed_amplitudes = [-4.876143648314624e-05, 0.057384102234558684] fermion_generator = uccsd_singlet_generator(packed_amplitudes, n_qubits,
from openfermion.utils import hermitian_conjugated from forestopenfermion import qubitop_to_pyquilpauli, pyquilpauli_to_qubitop from pyquil.quil import Program from pyquil.gates import X from pyquil.paulis import exponentiate from pyquil.api import QVMConnection hubbard_hamiltonian = FermionOperator() spatial_orbitals = 4 for i in range(spatial_orbitals): electron_hop_alpha = FermionOperator(((2*i, 1), (2*((i+1) % spatial_orbitals), 0))) electron_hop_beta = FermionOperator(((2*i+1, 1), ((2 * ((i+1) % spatial_orbitals) + 1), 0))) hubbard_hamiltonian += -1 * (electron_hop_alpha + hermitian_conjugated(electron_hop_alpha)) hubbard_hamiltonian += -1 * (electron_hop_beta + hermitian_conjugated(electron_hop_beta)) hubbard_hamiltonian += FermionOperator(((2*i, 1), (2*i, 0), (2*i+1, 1), (2*i+1, 0)), 4.0) hubbard_term_generator = jordan_wigner(hubbard_hamiltonian) pyquil_hubbard_generator = qubitop_to_pyquilpauli(hubbard_term_generator) localized_electrons_program = Program() localized_electrons_program.inst([X(0), X(1)]) pyquil_program = Program() for term in pyquil_hubbard_generator.terms: pyquil_program += exponentiate(0.1*term) print (localized_electrons_program + pyquil_program) qvm = QVMConnection() print(qvm.run(pyquil_program, [0], 10)) wf = qvm.wavefunction(pyquil_program) print(wf)
def test_x(self): pauli_x = QubitOperator(((2, 'X'),)) transmed_x = reverse_jordan_wigner(pauli_x) retransmed_x = jordan_wigner(transmed_x) self.assertTrue(pauli_x == retransmed_x)
manual_option="""**RELCCSD *CCENER .NOSDT""" molecule = MolecularData_Dirac(geometry=geometry, basis=basis, multiplicity=multiplicity, charge=charge, description=description, relativistic=relativistic, data_directory=data_directory) molecule = run_dirac(molecule, manual_option=manual_option, relativistic=relativistic, delete_input=delete_input, delete_xyz=delete_xyz, delete_output=delete_output, delete_MRCONEE=delete_MRCONEE, delete_MDCINT=delete_MDCINT, delete_FCIDUMP=delete_FCIDUMP, run_ccsd=run_ccsd) molecular_hamiltonian = molecule.get_molecular_hamiltonian()[0] qubit_hamiltonian = jordan_wigner(molecular_hamiltonian) evs = eigenspectrum(qubit_hamiltonian) print('Hartree-Fock energy of {} Hartree.'.format(molecule.get_energies()[0])) print('MP2 energy of {} Hartree.'.format(molecule.get_energies()[1])) print('CCSD energy of {} Hartree.'.format(molecule.get_energies()[2])) print('Solving the Qubit Hamiltonian (Jordan-Wigner): \n {}'.format(evs))
def get_Energy(bond_length): #パウリ演算子の準備 nqubits = 4 pI = np.array([[1 + 0.0j, 0 + 0.0j], [0 + 0.0j, 1 + 0.0j]]) pX = np.array([[0 + 0.0j, 1 + 0.0j], [1 + 0.0j, 0 + 0.0j]]) pZ = np.array([[1 + 0.0j, 0 + 0.0j], [0 + 0.0j, -1 + 0.0j]]) pY = np.array([[0 + 0.0j, -1.0j], [0.0 + 1.0j, 0.0 + 0.0j]]) pHad = (pX + pZ) / np.sqrt(2) pP0 = (pI + pZ) / 2 pP1 = (pI - pZ) / 2 #任意の状態に演算できるように準備 X = [1] * (nqubits) Y = [1] * (nqubits) Z = [1] * (nqubits) H = [1] * (nqubits) P0 = [1] * (nqubits) P1 = [1] * (nqubits) for i in range(nqubits): for j in range(nqubits): if (i != j): X[i] = np.kron(pI, X[i]) Y[i] = np.kron(pI, Y[i]) Z[i] = np.kron(pI, Z[i]) H[i] = np.kron(pI, H[i]) P0[i] = np.kron(pI, P0[i]) P1[i] = np.kron(pI, P1[i]) else: X[i] = np.kron(pX, X[i]) Y[i] = np.kron(pY, Y[i]) Z[i] = np.kron(pZ, Z[i]) H[i] = np.kron(pHad, H[i]) P0[i] = np.kron(pP0, P0[i]) P1[i] = np.kron(pP1, P1[i]) Ide = np.eye(2**nqubits) #2量子ゲートの準備 CZ = [[0 for i in range(nqubits)] for j in range(nqubits)] CX = [[0 for i in range(nqubits)] for j in range(nqubits)] for i in range(nqubits): for j in range(nqubits): CZ[i][j] = (P0[i] + np.dot(P1[i], Z[j])) CX[i][j] = (P0[i] + np.dot(P1[i], X[j])) #変分量子ゲートの準備 def iSWAP(target1, target2, angle): return expm(-0.5 * angle * 1.j * CX[target1][target2]) def iCPHASE(target1, target2, angle): return expm(-0.5 * angle * 1.j * CZ[target1][target2]) def RX(target, angle): return expm(-0.5 * angle * 1.j * X[target]) def RY(target, angle): return expm(-0.5 * angle * 1.j * Y[target]) def RZ(target, angle): return expm(-0.5 * angle * 1.j * Z[target]) #初期状態の準備 def StateZeros(nqubits): State = np.zeros(2**nqubits) State[9] = 1 return State #求めるハミルトニアンのデータ geometry = [["H", [0, 0, 0]], ["H", [0, 0, bond_length]]] basis = "sto-3g" multiplicity = 1 charge = 0 description = "test" #str() molecule = MolecularData(geometry, basis, multiplicity, charge, description) #求めるハミルトニアンのJW変換 molecule = run_psi4(molecule) jw_hamiltonian = jordan_wigner( get_fermion_operator(molecule.get_molecular_hamiltonian())) jw_matrix = get_sparse_operator(jw_hamiltonian) #量子回路 n_param = 12 def QubitPQC(phi): state = StateZeros(4) state = np.dot(iSWAP(0, 1, phi[0]), state) state = np.dot(iCPHASE(0, 1, phi[1]), state) state = np.dot(iSWAP(2, 3, phi[2]), state) state = np.dot(iCPHASE(2, 3, phi[3]), state) state = np.dot(iSWAP(1, 2, phi[4]), state) state = np.dot(iCPHASE(1, 2, phi[5]), state) state = np.dot(iSWAP(0, 1, phi[6]), state) state = np.dot(iCPHASE(0, 1, phi[7]), state) state = np.dot(iSWAP(2, 3, phi[8]), state) state = np.dot(iCPHASE(2, 3, phi[9]), state) state = np.dot(iSWAP(1, 2, phi[10]), state) state = np.dot(iCPHASE(1, 2, phi[11]), state) return state #エネルギーの期待値を求める関数 def ExpectVal(Operator, State): BraState = np.conjugate(State.T) #列ベクトルを行ベクトルへ変換 tmp = BraState.dot(Operator.dot(State)) #行列を列ベクトルと行ベクトルではさむ return np.real(tmp) #要素の実部を取り出す #VQEの実行 def cost(phi): return ExpectVal(jw_matrix, QubitPQC(phi)) init = np.random.rand(n_param) res = scipy.optimize.minimize(cost, init, method='Powell') molecule = run_psi4(molecule, run_scf=1, run_fci=1) eigenenergies, eigenvecs = eigs(jw_matrix) return cost(res.x)
from openfermion.ops import QubitOperator from openfermion.transforms import jordan_wigner, reverse_jordan_wigner # Initialize QubitOperator. my_operator = QubitOperator('X0 Y1 Z2', 88.) my_operator += QubitOperator('Z1 Z4', 3.17) print (my_operator) # Map QubitOperator to a FermionOperator. mapped_operator = reverse_jordan_wigner(my_operator) print (mapped_operator) # Map the operator back to qubits and make sure it is the same. back_to_normal = jordan_wigner(mapped_operator) back_to_normal.compress() print (back_to_normal)
def test_jordan_wigner_dual_basis_hamiltonian_default_to_jellium(self): grid = Grid(dimensions=3, scale=1.0, length=2) self.assertTrue(jordan_wigner_dual_basis_hamiltonian(grid) == jordan_wigner(jellium_model(grid, plane_wave=False)))
def test_term(self): transmed_term = reverse_jordan_wigner(self.term) retransmed_term = jordan_wigner(transmed_term) self.assertTrue(self.term == retransmed_term)
def observable(fermion_ops, init_term=0, mapping="jordan_wigner", wires=None): r"""Builds the Fermion many-body observable whose expectation value can be measured in PennyLane. The second-quantized operator of the Fermion many-body system can combine one-particle and two-particle operators as in the case of electronic Hamiltonians :math:`\hat{H}`: .. math:: \hat{H} = \sum_{\alpha, \beta} \langle \alpha \vert \hat{t}^{(1)} + \cdots + \hat{t}^{(n)} \vert \beta \rangle ~ \hat{c}_\alpha^\dagger \hat{c}_\beta + \frac{1}{2} \sum_{\alpha, \beta, \gamma, \delta} \langle \alpha, \beta \vert \hat{v}^{(1)} + \cdots + \hat{v}^{(n)} \vert \gamma, \delta \rangle ~ \hat{c}_\alpha^\dagger \hat{c}_\beta^\dagger \hat{c}_\gamma \hat{c}_\delta In the latter equations the indices :math:`\alpha, \beta, \gamma, \delta` run over the basis of single-particle states. The operators :math:`\hat{c}^\dagger` and :math:`\hat{c}` are the particle creation and annihilation operators, respectively. :math:`\langle \alpha \vert \hat{t} \vert \beta \rangle` denotes the matrix element of the single-particle operator :math:`\hat{t}` entering the observable. For example, in electronic structure calculations, this is the case for: the kinetic energy operator, the nuclei Coulomb potential, or any other external fields included in the Hamiltonian. On the other hand, :math:`\langle \alpha, \beta \vert \hat{v} \vert \gamma, \delta \rangle` denotes the matrix element of the two-particle operator :math:`\hat{v}`, for example, the Coulomb interaction between the electrons. - The observable is built by adding the operators :math:`\sum_{\alpha, \beta} t_{\alpha\beta}^{(i)} \hat{c}_\alpha^\dagger \hat{c}_\beta` and :math:`\frac{1}{2} \sum_{\alpha, \beta, \gamma, \delta} v_{\alpha\beta\gamma\delta}^{(i)} \hat{c}_\alpha^\dagger \hat{c}_\beta^\dagger \hat{c}_\gamma \hat{c}_\delta`. - Second-quantized operators contributing to the many-body observable must be represented using the `FermionOperator <https://github.com/quantumlib/OpenFermion/blob/master/docs/ tutorials/intro_to_openfermion.ipynb>`_ data structure as implemented in OpenFermion. See the functions :func:`~.one_particle` and :func:`~.two_particle` to build the FermionOperator representations of one-particle and two-particle operators. - The function uses tools of `OpenFermion <https://github.com/quantumlib/OpenFermion>`_ to map the resulting fermionic Hamiltonian to the basis of Pauli matrices via the Jordan-Wigner or Bravyi-Kitaev transformation. Finally, the qubit operator is converted to a PennyLane observable by the function :func:`~.convert_observable`. Args: fermion_ops (list[FermionOperator]): list containing the FermionOperator data structures representing the one-particle and/or two-particle operators entering the many-body observable init_term (float): Any quantity required to initialize the many-body observable. For example, this can be used to pass the nuclear-nuclear repulsion energy :math:`V_{nn}` which is typically included in the electronic Hamiltonian of molecules. mapping (str): Specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. wires (Wires, list, tuple, dict): Custom wire mapping used to convert the qubit operator to an observable measurable in a PennyLane ansatz. For types Wires/list/tuple, each item in the iterable represents a wire label corresponding to the qubit number equal to its index. For type dict, only int-keyed dict (for qubit-to-wire conversion) is accepted. If None, will use identity map (e.g. 0->0, 1->1, ...). Returns: pennylane.Hamiltonian: the fermionic-to-qubit transformed observable **Example** >>> t = FermionOperator("0^ 0", 0.5) + FermionOperator("1^ 1", 0.25) >>> v = FermionOperator("1^ 0^ 0 1", -0.15) + FermionOperator("2^ 0^ 2 0", 0.3) >>> print(observable([t, v], mapping="jordan_wigner")) (0.2625) [I0] + (-0.1375) [Z0] + (-0.0875) [Z1] + (-0.0375) [Z0 Z1] + (0.075) [Z2] + (-0.075) [Z0 Z2] """ if mapping.strip().lower() not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.".format(mapping) ) # Initialize the FermionOperator mb_obs = FermionOperator("") * init_term for ops in fermion_ops: if not isinstance(ops, FermionOperator): raise TypeError( "Elements in the lists are expected to be of type 'FermionOperator'; got {}".format( type(ops) ) ) mb_obs += ops # Map the fermionic operator to a qubit operator if mapping.strip().lower() == "bravyi_kitaev": return structure.convert_observable(bravyi_kitaev(mb_obs), wires=wires) return structure.convert_observable(jordan_wigner(mb_obs), wires=wires)
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 test_yzxz(self): yzxz = QubitOperator(((0, 'Y'), (1, 'Z'), (2, 'X'), (3, 'Z'))) transmed_yzxz = reverse_jordan_wigner(yzxz) retransmed_yzxz = jordan_wigner(transmed_yzxz) self.assertTrue(yzxz == retransmed_yzxz)
def observable(me_table, init_term=0, mapping="jordan_wigner"): r"""Builds the many-body observable whose expectation value can be measured in PennyLane. This function can be used to build second-quantized operators in the basis of single-particle states (e.g., HF states) and to transform them into PennyLane observables. In general, single- and two-particle operators can be expanded in a truncated set of orbitals that define an active space, .. math:: &&\hat A = \sum_{\alpha \leq 2n_\mathrm{docc}} \langle \alpha \vert \hat{\mathcal{A}} \vert \alpha \rangle ~ \hat{n}_\alpha + \sum_{\alpha, \beta ~ \in ~ \mathrm{active~space}} \langle \alpha \vert \hat{\mathcal{A}} \vert \beta \rangle ~ \hat{c}_\alpha^\dagger\hat{c}_\beta \\ &&\hat B = \frac{1}{2} \left\{ \sum_{\alpha, \beta \leq 2n_\mathrm{docc}} \langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \beta, \alpha \rangle ~ \hat{n}_\alpha \hat{n}_\beta + \sum_{\alpha, \beta, \gamma, \delta ~ \in ~ \mathrm{active~space}} \langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \gamma, \delta \rangle ~ \hat{c}_{\alpha}^\dagger \hat{c}_{\beta}^\dagger \hat{c}_{\gamma} \hat{c}_{\delta} \right\}. In the latter equations :math:`n_\mathrm{docc}` denotes the doubly-occupied orbitals, if any, not included in the active space and :math:`\langle \alpha \vert \hat{\mathcal{A}} \vert \beta \rangle` and :math:`\langle \alpha, \beta \vert\hat{\mathcal{B}} \vert \gamma, \delta \rangle` are the matrix elements of the one- and two-particle operators :math:`\hat{\mathcal{A}}` and :math:`\hat{\mathcal{B}}`, respectively. The function utilizes tools of `OpenFermion <https://github.com/quantumlib/OpenFermion>`_ to build the second-quantized operator and map it to basis of Pauli matrices via the Jordan-Wigner or Bravyi-Kitaev transformation. Finally, the qubit operator is converted to a a PennyLane observable by the function :func:`~.convert_observable`. Args: me_table (array[float]): Numpy array with the table of matrix elements. For a single-particle operator this array will have shape ``(me_table.shape[0], 3)`` with each row containing the indices :math:`\alpha`, :math:`\beta` and the matrix element :math:`\langle \alpha \vert \hat{\mathcal{A}}\vert \beta \rangle`. For a two-particle operator this array will have shape ``(me_table.shape[0], 5)`` with each row containing the indices :math:`\alpha`, :math:`\beta`, :math:`\gamma`, :math:`\delta` and the matrix elements :math:`\langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \gamma, \delta \rangle`. init_term: the contribution of doubly-occupied orbitals, if any, or other quantity required to initialize the many-body observable. mapping (str): specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. Returns: pennylane.Hamiltonian: the fermionic-to-qubit transformed observable **Example** >>> s2_matrix_elements, init_term = get_spin2_matrix_elements('h2', './pyscf/sto-3g') >>> s2_obs = observable(s2_matrix_elements, init_term=init_term) >>> print(s2_obs) (0.75) [I0] + (0.375) [Z1] + (-0.375) [Z0 Z1] + (0.125) [Z0 Z2] + (0.375) [Z0] + (-0.125) [Z0 Z3] + (-0.125) [Z1 Z2] + (0.125) [Z1 Z3] + (0.375) [Z2] + (0.375) [Z3] + (-0.375) [Z2 Z3] + (0.125) [Y0 X1 Y2 X3] + (0.125) [Y0 Y1 X2 X3] + (0.125) [Y0 Y1 Y2 Y3] + (-0.125) [Y0 X1 X2 Y3] + (-0.125) [X0 Y1 Y2 X3] + (0.125) [X0 X1 X2 X3] + (0.125) [X0 X1 Y2 Y3] + (0.125) [X0 Y1 X2 Y3] """ if mapping.strip().lower() not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.". format(mapping)) sp_op_shape = (3, ) tp_op_shape = (5, ) for i_table in me_table: if np.array(i_table).shape not in (sp_op_shape, tp_op_shape): raise ValueError( "expected entries of 'me_table' to be of shape (3,) or (5,) ; got {}" .format(np.array(i_table).shape)) # Initialize the FermionOperator mb_obs = FermionOperator() + FermionOperator("") * init_term for i in me_table: if i.shape == (5, ): # two-particle operator mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 1), (int(i[2]), 0), (int(i[3]), 0)), i[4]) elif i.shape == (3, ): # single-particle operator mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 0)), i[2]) # Map the fermionic operator to a qubit operator if mapping.strip().lower() == "bravyi_kitaev": return structure.convert_observable(bravyi_kitaev(mb_obs)) return structure.convert_observable(jordan_wigner(mb_obs))
def observable(matrix_elements, init_term=0, mapping="jordan_wigner", wires=None): r"""Builds the many-body observable whose expectation value can be measured in PennyLane. This function can be used to build second-quantized operators in the basis of single-particle states (e.g., HF states) and to transform them into PennyLane observables. In general, the many-body observable :math:`\hat{O}` can combine one-particle and two-particle operators, as in the case for electronic Hamiltonians: .. math:: \hat{O} = \sum_{\alpha, \beta} \langle \alpha \vert \hat{t}^{(1)} + \cdots + \hat{t}^{(n)} \vert \beta \rangle ~ \hat{c}_\alpha^\dagger \hat{c}_\beta + \frac{1}{2} \sum_{\alpha, \beta, \gamma, \delta} \langle \alpha, \beta \vert \hat{v}^{(1)} + \cdots + \hat{v}^{(n)} \vert \gamma, \delta \rangle ~ \hat{c}_\alpha^\dagger \hat{c}_\beta^\dagger \hat{c}_\gamma \hat{c}_\delta In the latter equations the indices :math:`\alpha, \beta, \gamma, \delta` run over the basis of single-particle states. The operators :math:`\hat{c}^\dagger` and :math:`\hat{c}` are the particle creation and annihilation operators, respectively. :math:`\langle \alpha \vert \hat{t} \vert \beta \rangle` denotes the matrix element of the single-particle operator :math:`\hat{t}` entering the observable. For example, in electronic structure calculations, this is the case for: the kinetic energy operator, the nuclei Coulomb potential, or any other external fields included in the model Hamiltonian. On the other hand, :math:`\langle \alpha, \beta \vert \hat{v} \vert \gamma, \delta \rangle` denotes the matrix element of the two-particle operator :math:`\hat{v}`, for example, the Coulomb interaction between the electrons. If an `active space <https://en.wikipedia.org/wiki/Complete_active_space>`_ (see :func:`~.active_space`) is defined, the observable is expanded over the truncated basis of active orbitals. The contribution of core orbitals can be passed to the function using the keyword argument ``init_term``. The function uses tools of `OpenFermion <https://github.com/quantumlib/OpenFermion>`_ to build the second-quantized operator and map it to basis of Pauli matrices via the Jordan-Wigner or Bravyi-Kitaev transformation. Finally, the qubit operator is converted to a a PennyLane observable by the function :func:`~.convert_observable`. Args: matrix_elements (list(array[float])): list containing 2D numpy arrays with the matrix elements of the operators :math:`\hat{t}` and :math:`\hat{v}`. For single-particle operators the :math:`i`-th array in the list will have shape ``(matrix_elements[i].shape[0], 3)`` with each row containing the indices :math:`\alpha`, :math:`\beta` and the matrix element :math:`\langle \alpha \vert \hat{t}^{(i)}\vert \beta \rangle`. For two-particle operators the :math:`j`-th array in the list will have shape ``(matrix_elements[j].shape[0], 5)`` with each row containing the indices :math:`\alpha`, :math:`\beta`, :math:`\gamma`, :math:`\delta` and the matrix element :math:`\langle \alpha, \beta \vert \hat{v}^{(j)}\vert \gamma, \delta \rangle`. init_term (float): the contribution of core orbitals, if any, or other quantity required to initialize the many-body observable. mapping (str): specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. wires (Wires, list, tuple, dict): Custom wire mapping used to convert the qubit operator to an observable measurable in a PennyLane ansatz. For types Wires/list/tuple, each item in the iterable represents a wire label corresponding to the qubit number equal to its index. For type dict, only int-keyed dict (for qubit-to-wire conversion) is accepted. If None, will use identity map (e.g. 0->0, 1->1, ...). Returns: pennylane.Hamiltonian: the fermionic-to-qubit transformed observable **Example** >>> t = np.array([[0., 0., 0.5], [1.0, 1.0, -0.5], [1.0, 0., 0.]]) >>> v = np.array([[ 0., 0., 0., 0., 0.25], [ 0., 1., 1., 0., -0.25], [ 1., 0., 0., 1., -0.5]]) >>> matrix_elements = [t, v] >>> print(observable(matrix_elements, init_term=1/4, mapping="bravyi_kitaev")) (0.15625) [I0] + (-0.15625) [Z0] + (0.34375) [Z0 Z1] + (-0.09375) [Z1] """ if mapping.strip().lower() not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.". format(mapping)) # Initialize the FermionOperator mb_obs = FermionOperator() + FermionOperator("") * init_term for table in matrix_elements: if len(table.shape) != 2: raise ValueError( "Expected dimension for arrays in 'matrix_elements' is 2; got {}" .format(table.shape)) if table.shape[1] not in (3, 5): raise ValueError( "Expected entries of matrix element tables to be of shape (3,) or (5,); got {}" .format(table.shape[1])) if table.shape[1] == 5: # two-particle operator for i in table: mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 1), (int(i[2]), 0), (int(i[3]), 0)), i[4] / 2) else: # single-particle operator for i in table: mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 0)), i[2]) # Map the fermionic operator to a qubit operator if mapping.strip().lower() == "bravyi_kitaev": return structure.convert_observable(bravyi_kitaev(mb_obs), wires=wires) return structure.convert_observable(jordan_wigner(mb_obs), wires=wires)
# ハミルトニアンを行列に変換 h = np.array([0, [], [[]], [[[]]], [[[[]]]]]) h[0] = fermionic_hamiltonian.terms[()] h[1] = np.zeros(nqubit) h[2] = np.zeros((nqubit, nqubit)) h[3] = np.zeros((nqubit, nqubit, nqubit)) h[4] = np.zeros((nqubit, nqubit, nqubit, nqubit)) for i, j in itertools.product(range(nqubit), repeat=2): h[2][i][j] = fermionic_hamiltonian.terms.get(((i, 1), (j, 0)), 0) for i, j, k, l in itertools.product(range(nqubit), repeat=4): h[4][i][j][k][l] = fermionic_hamiltonian.terms.get( ((i, 1), (j, 1), (k, 0), (l, 0)), 0) # FCI解を対角化により得る jw_hamiltonian = jordan_wigner(fermionic_hamiltonian) hamiltonian_matrix = get_sparse_operator(jw_hamiltonian) from scipy.sparse.linalg import eigsh elapsed_time = time.time() - start timer_start[0] = elapsed_time eigval, eigvec = eigsh(hamiltonian_matrix, k=num_states, which="SA") elapsed_time = time.time() - start timer_end[0] = elapsed_time for i in range(num_states): energy_fci_hist[i].append(eigval[i]) # ここからSVD
def observable(me_table, init_term=0, mapping="jordan_wigner", wires=None): r"""Builds the many-body observable whose expectation value can be measured in PennyLane. This function can be used to build second-quantized operators in the basis of single-particle states (e.g., HF states) and to transform them into PennyLane observables. In general, single- and two-particle operators can be expanded in a defined active space, .. math:: &&\hat A = \sum_{\alpha \leq 2n_\mathrm{docc}} \langle \alpha \vert \hat{\mathcal{A}} \vert \alpha \rangle ~ \hat{n}_\alpha + \sum_{\alpha, \beta ~ \in ~ \mathrm{active~space}} \langle \alpha \vert \hat{\mathcal{A}} \vert \beta \rangle ~ \hat{c}_\alpha^\dagger\hat{c}_\beta \\ &&\hat B = \frac{1}{2} \left\{ \sum_{\alpha, \beta \leq 2n_\mathrm{docc}} \langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \beta, \alpha \rangle ~ \hat{n}_\alpha \hat{n}_\beta + \sum_{\alpha, \beta, \gamma, \delta ~ \in ~ \mathrm{active~space}} \langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \gamma, \delta \rangle ~ \hat{c}_{\alpha}^\dagger \hat{c}_{\beta}^\dagger \hat{c}_{\gamma} \hat{c}_{\delta} \right\}. In the latter equations :math:`n_\mathrm{docc}` denotes the doubly-occupied orbitals, if any, not included in the active space and :math:`\langle \alpha \vert \hat{\mathcal{A}} \vert \beta \rangle` and :math:`\langle \alpha, \beta \vert\hat{\mathcal{B}} \vert \gamma, \delta \rangle` are the matrix elements of the one- and two-particle operators :math:`\hat{\mathcal{A}}` and :math:`\hat{\mathcal{B}}`, respectively. The function utilizes tools of `OpenFermion <https://github.com/quantumlib/OpenFermion>`_ to build the second-quantized operator and map it to basis of Pauli matrices via the Jordan-Wigner or Bravyi-Kitaev transformation. Finally, the qubit operator is converted to a a PennyLane observable by the function :func:`~.convert_observable`. Args: me_table (array[float]): Numpy array with the table of matrix elements. For single-particle operators this array will have shape ``(me_table.shape[0], 3)`` with each row containing the indices :math:`\alpha`, :math:`\beta` and the matrix element :math:`\langle \alpha \vert \hat{\mathcal{A}}\vert \beta \rangle`. For two-particle operators this array will have shape ``(me_table.shape[0], 5)`` with each row containing the indices :math:`\alpha`, :math:`\beta`, :math:`\gamma`, :math:`\delta` and the matrix elements :math:`\langle \alpha, \beta \vert \hat{\mathcal{B}} \vert \gamma, \delta \rangle`. init_term: the contribution of doubly-occupied orbitals, if any, or other quantity required to initialize the many-body observable. mapping (str): specifies the fermion-to-qubit mapping. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. wires (Wires, list, tuple, dict): Custom wire mapping used to convert the qubit operator to an observable measurable in a PennyLane ansatz. For types Wires/list/tuple, each item in the iterable represents a wire label corresponding to the qubit number equal to its index. For type dict, only int-keyed dict (for qubit-to-wire conversion) is accepted. If None, will use identity map (e.g. 0->0, 1->1, ...). Returns: pennylane.Hamiltonian: the fermionic-to-qubit transformed observable **Example** >>> table = np.array([[0.0, 0.0, 0.4], [1.0, 1.0, -0.5], [1.0, 0.0, 0.0]]) >>> print(observable(table, init_term=1 / 4, mapping="bravyi_kitaev")) (0.2) [I0] + (-0.2) [Z0] + (0.25) [Z0 Z1] >>> print(observable(table, init_term=1 / 4, mapping="bravyi_kitaev", wires=['w0','w1'])) (0.2) [Iw0] + (-0.2) [Zw0] + (0.25) [Zw0 Zw1] """ if mapping.strip().lower() not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.". format(mapping)) sp_op_shape = (3, ) tp_op_shape = (5, ) for i_table in me_table: if np.array(i_table).shape not in (sp_op_shape, tp_op_shape): raise ValueError( "expected entries of 'me_table' to be of shape (3,) or (5,) ; got {}" .format(np.array(i_table).shape)) # Initialize the FermionOperator mb_obs = FermionOperator() + FermionOperator("") * init_term for i in me_table: if i.shape == (5, ): # two-particle operator mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 1), (int(i[2]), 0), (int(i[3]), 0)), i[4]) elif i.shape == (3, ): # single-particle operator mb_obs += FermionOperator(((int(i[0]), 1), (int(i[1]), 0)), i[2]) # Map the fermionic operator to a qubit operator if mapping.strip().lower() == "bravyi_kitaev": return structure.convert_observable(bravyi_kitaev(mb_obs), wires=wires) return structure.convert_observable(jordan_wigner(mb_obs), wires=wires)
def generate(self, inputParams): g = str(inputParams['geometry']) basis = inputParams['basis'] moleculeGeom = geometry(g) psi4.set_options({ 'basis': 'sto-3g', 'scf_type': 'pk', 'mp2_type': 'conv', 'e_convergence': 1e-8, 'd_convergence': 1e-8 }) scf_e, scf_wfn = psi4.energy('scf', return_wfn=True) E_nucl = moleculeGeom.nuclear_repulsion_energy() # Get MO coefficients from SCF wavefunction # ==> ERIs <== # Create instance of MintsHelper class: mints = psi4.core.MintsHelper(scf_wfn.basisset()) nbf = mints.nbf() # number of basis functions nso = 2 * nbf # number of spin orbitals nalpha = scf_wfn.nalpha() # number of alpha electrons nbeta = scf_wfn.nbeta() # number of beta electrons nocc = nalpha + nbeta # number of occupied orbitals nvirt = 2 * nbf - nocc # number of virtual orbitals list_occ_alpha = np.asarray(scf_wfn.occupation_a()) list_occ_beta = np.asarray(scf_wfn.occupation_b()) # Get orbital energies eps_a = np.asarray(scf_wfn.epsilon_a()) eps_b = np.asarray(scf_wfn.epsilon_b()) eps = np.append(eps_a, eps_b) # Get orbital coefficients: Ca = np.asarray(scf_wfn.Ca()) Cb = np.asarray(scf_wfn.Cb()) C = np.block([[Ca, np.zeros_like(Cb)], [np.zeros_like(Ca), Cb]]) # Get the two electron integrals using MintsHelper Ints = np.asarray(mints.ao_eri()) def spin_block_tei(I): """ Function that spin blocks two-electron integrals Using np.kron, we project I into the space of the 2x2 identity, tranpose the result and project into the space of the 2x2 identity again. This doubles the size of each axis. The result is our two electron integral tensor in the spin orbital form. """ identity = np.eye(2) I = np.kron(identity, I) return np.kron(identity, I.T) # Spin-block the two electron integral array I_spinblock = spin_block_tei(Ints) # Converts chemist's notation to physicist's notation, and antisymmetrize # (pq | rs) ---> <pr | qs> # Physicist's notation tmp = I_spinblock.transpose(0, 2, 1, 3) # Antisymmetrize: # <pr||qs> = <pr | qs> - <pr | sq> gao = tmp - tmp.transpose(0, 1, 3, 2) gmo = np.einsum( 'pQRS, pP -> PQRS', np.einsum( 'pqRS, qQ -> pQRS', np.einsum('pqrS, rR -> pqRS', np.einsum('pqrs, sS -> pqrS', gao, C), C), C), C) # -------- 0-body term: Hamiltonian_0body = E_nucl # -------- 1-body term: # Ca* # Build core Hamiltonian T = np.asarray(mints.ao_kinetic()) V = np.asarray(mints.ao_potential()) H_core_ao = T + V # -- check which one more efficient (matmul vs einsum) # H_core_mo = np.matmul(Ca.T,np.matmul(H_core_ao,Ca))) H_core_mo = np.einsum('ij, jk, kl -> il', Ca.T, H_core_ao, Ca) H_core_mo_alpha = H_core_mo H_core_mo_beta = H_core_mo # ---- this version breaks is we permuted SCF eigvecs # Hamiltonian_1body = np.block([ # [ H_core_mo_alpha , np.zeros_like(H_core_mo_alpha)], # [np.zeros_like(H_core_mo_beta) , H_core_mo_beta ]]) # # --- th is version is safer than above (H_1b is permutted correctly if eigvecs are permutted) Hamiltonian_1body_ao = np.block([[H_core_ao, np.zeros_like(H_core_ao)], [np.zeros_like(H_core_ao), H_core_ao]]) Hamiltonian_1body = np.einsum('ij, jk, kl -> il', C.T, Hamiltonian_1body_ao, C) Hamiltonian_2body = gmo MSO_frozen_list = ast.literal_eval(inputParams['frozen-spin-orbitals']) MSO_active_list = ast.literal_eval(inputParams['active-spin-orbitals']) n_frozen = len(MSO_frozen_list) n_active = len(MSO_active_list) # ----- 0-body frozen-core: Hamiltonian_fc_0body = E_nucl for a in range(n_frozen): ia = MSO_frozen_list[a] Hamiltonian_fc_0body += Hamiltonian_1body[ia, ia] for b in range(a): ib = MSO_frozen_list[b] Hamiltonian_fc_0body += gmo[ia, ib, ia, ib] # --- 1-body frozen-core: Hamiltonian_fc_1body = np.zeros((n_active, n_active)) Hamiltonian_fc_1body_tmp = np.zeros((n_active, n_active)) for p in range(n_active): ip = MSO_active_list[p] for q in range(n_active): iq = MSO_active_list[q] Hamiltonian_fc_1body[p, q] = Hamiltonian_1body[ip, iq] #Hamiltonian_fc_1body_tmp[p,q] = Hamiltonian_1body[ip,iq] for a in range(n_frozen): ia = MSO_frozen_list[a] Hamiltonian_fc_1body[p, q] += gmo[ia, ip, ia, iq] # ------- 2-body frozen-core: Hamiltonian_fc_2body = np.zeros( (n_active, n_active, n_active, n_active)) for p in range(n_active): ip = MSO_active_list[p] for q in range(n_active): iq = MSO_active_list[q] for r in range(n_active): ir = MSO_active_list[r] for ss in range(n_active): iss = MSO_active_list[ss] #Hamiltonian_fc_2body[p,q,r,ss]= 0.25* gmo[ip,iq,ir,iss] Hamiltonian_fc_2body[p, q, r, ss] = gmo[ip, iq, ir, iss] #Hamiltonian_fc_2body[p,q,r,ss]= 0.25* gmo[ip,iq,iss,ir] Hamiltonian_fc_2body_tmp = 0.25 * Hamiltonian_fc_2body.transpose( 0, 1, 3, 2) fc_ham = InteractionOperator(Hamiltonian_fc_0body, Hamiltonian_fc_1body, Hamiltonian_fc_2body_tmp) fc_fermion = get_fermion_operator(fc_ham) src = xaccvqe.get_fermion_compiler_source(fc_fermion) inputParams['fermion-source'] = src fc_jw = jordan_wigner(fc_fermion) op = xaccvqe.QubitOperator2XACC(fc_jw) print(op) return op
molecule = run_pyscf(molecule, run_scf=run_scf, run_mp2=run_mp2, run_cisd=run_cisd, run_ccsd=run_ccsd, run_fci=run_fci) # Get the Hamiltonian in an active space. molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(active_space_start), active_indices=range(active_space_start, active_space_stop)) # Map operator to fermions and qubits. fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian) qubit_hamiltonian = jordan_wigner(fermion_hamiltonian) # compress removes 0 entries. qubit_hamiltonian is a qubit_operator qubit_hamiltonian.compress() # Test OpenQASM qc_list = [] def _process(circuit): qc_list.append(circuit) backend = OpenQASMEngine(_process) compiler_engine = uccsd_trotter_engine(compiler_backend=backend)
def test_ucc_h2_singlet(self): geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered( get_fermion_operator(self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian) # Test UCCSD for accuracy against FCI using loaded t amplitudes. ucc_operator = uccsd_generator(self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps) hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) uccsd_sparse = jordan_wigner_sparse(ucc_operator) uccsd_state = scipy.sparse.linalg.expm_multiply(uccsd_sparse, hf_state) expected_uccsd_energy = expectation(self.hamiltonian_matrix, uccsd_state) self.assertAlmostEqual(expected_uccsd_energy, self.molecule.fci_energy, places=4) print("UCCSD ENERGY: {}".format(expected_uccsd_energy)) # Test CCSD singlet for precise match against FCI using loaded t # amplitudes packed_amplitudes = uccsd_singlet_get_packed_amplitudes( self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps, self.molecule.n_qubits, self.molecule.n_electrons) ccsd_operator = uccsd_singlet_generator(packed_amplitudes, self.molecule.n_qubits, self.molecule.n_electrons, anti_hermitian=False) ccsd_sparse_r = jordan_wigner_sparse(ccsd_operator) ccsd_sparse_l = jordan_wigner_sparse( -hermitian_conjugated(ccsd_operator)) ccsd_state_r = scipy.sparse.linalg.expm_multiply( ccsd_sparse_r, hf_state) ccsd_state_l = scipy.sparse.linalg.expm_multiply( ccsd_sparse_l, hf_state) expected_ccsd_energy = ccsd_state_l.conjugate().dot( self.hamiltonian_matrix.dot(ccsd_state_r)) self.assertAlmostEqual(expected_ccsd_energy, self.molecule.fci_energy)
def test_get_qubit_expectations_through_expectation_method(self): qubit_operator = jordan_wigner(self.hamiltonian) test_energy = self.rdm.expectation(qubit_operator) self.assertLess(abs(test_energy - self.cisd_energy), EQ_TOLERANCE)
def test_coefficients(self): # Test that the coefficients post-JW transform are as claimed in paper. grid = Grid(dimensions=2, length=3, scale=2.) spinless = 1 n_orbitals = grid.num_points() n_qubits = (2**(1 - spinless)) * n_orbitals volume = grid.volume_scale() # Kinetic operator. kinetic = dual_basis_kinetic(grid, spinless) qubit_kinetic = jordan_wigner(kinetic) # Potential operator. potential = dual_basis_potential(grid, spinless) qubit_potential = jordan_wigner(potential) # Check identity. identity = tuple() kinetic_coefficient = qubit_kinetic.terms[identity] potential_coefficient = qubit_potential.terms[identity] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = momentum_vector(indices, grid) paper_kinetic_coefficient += float(n_qubits) * momenta.dot( momenta) / float(4. * n_orbitals) if momenta.any(): potential_contribution = -numpy.pi * float(n_qubits) / float( 2. * momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp. for p in range(n_qubits): zp = ((p, 'Z'), ) kinetic_coefficient = qubit_kinetic.terms[zp] potential_coefficient = qubit_potential.terms[zp] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = momentum_vector(indices, grid) paper_kinetic_coefficient -= momenta.dot(momenta) / float( 4. * n_orbitals) if momenta.any(): potential_contribution = numpy.pi / float( momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp Zq. if spinless: spins = [None] else: spins = [0, 1] for indices_a in grid.all_points_indices(): for indices_b in grid.all_points_indices(): paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. position_a = position_vector(indices_a, grid) position_b = position_vector(indices_b, grid) differences = position_b - position_a for spin_a in spins: for spin_b in spins: p = orbital_id(grid, indices_a, spin_a) q = orbital_id(grid, indices_b, spin_b) if p == q: continue zpzq = ((min(p, q), 'Z'), (max(p, q), 'Z')) if zpzq in qubit_potential.terms: potential_coefficient = qubit_potential.terms[zpzq] for indices_c in grid.all_points_indices(): momenta = momentum_vector(indices_c, grid) if momenta.any(): potential_contribution = numpy.pi * numpy.cos( differences.dot(momenta)) / float( momenta.dot(momenta) * volume) paper_potential_coefficient += ( potential_contribution) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient)
def test_plane_wave_period_cutoff(self): # TODO: After figuring out the correct formula for period cutoff for # dual basis, change period_cutoff to default, and change # h_1 to also accept period_cutoff for real integration 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]] spinless_set = [True, False] scale = 8 * 1. for dim in dims: for geometry in geometry_sets[dim[0]]: for spinless in spinless_set: grid = Grid(dimensions=dim[0], scale=scale, length=2) period_cutoff = grid.volume_scale() ** (1. / grid.dimensions) h_1 = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True, include_constant=False, e_cutoff=None, fieldlines=dim[1]) jw_1 = jordan_wigner(h_1) spectrum_1 = eigenspectrum(jw_1) h_2 = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) jw_2 = jordan_wigner(h_2) spectrum_2 = eigenspectrum(jw_2) max_diff = numpy.amax(numpy.absolute(spectrum_1 - spectrum_2)) # Checks if non-periodic and periodic cases are different. self.assertGreater(max_diff, 0.) # TODO: This is only for code coverage. Remove after having real # integration test. momentum_hamiltonian = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, fieldlines=dim[1]) position_hamiltonian = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=False, include_constant=False, e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff, 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. 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 verstraete_cirac_2d_square(operator, x_dimension, y_dimension, add_auxiliary_hamiltonian=True, snake=False): """Apply the Verstraete-Cirac transform on a 2-d square lattice. Note that this transformation adds one auxiliary fermionic mode for each mode already present, and hence it doubles the number of qubits needed to represent the system. Currently only supports even values of x_dimension and only works for spinless models. Args: operator (FermionOperator): The operator to transform. x_dimension (int): The number of columns of the grid. y_dimension (int): The number of rows of the grid. snake (bool, optional): Indicates whether the fermions are already ordered according to the 2-d "snake" ordering. If False, we assume they are in "lexicographic" order by row and column index. Default is False. Returns: transformed_operator: A QubitOperator. """ if x_dimension % 2 != 0: raise NotImplementedError('Currently only even x_dimension ' 'is supported.') # Obtain the vertical edges of the snake ordering vert_edges = vertical_edges_snake(x_dimension, y_dimension) # Initialize a coefficient to scale the auxiliary Hamiltonian by. # The gap of the auxiliary Hamiltonian needs to be large enough to # to ensure the ground state of the original operator is preserved. aux_ham_coefficient = 1. transformed_operator = QubitOperator() for term in operator.terms: indices = [ladder_operator[0] for ladder_operator in term] raise_or_lower = [ladder_operator[1] for ladder_operator in term] coefficient = operator.terms[term] # If the indices aren't in snake order, we need to convert them if not snake: indices = [ lexicographic_index_to_snake_index(index, x_dimension, y_dimension) for index in indices ] # Convert the indices to indices of system qubits in the combined # system, which includes the auxiliary qubits interleaved transformed_indices = [expand_sys_index(index) for index in indices] # Initialize the transformed term as a FermionOperator transformed_term = FermionOperator( tuple(zip(transformed_indices, raise_or_lower)), coefficient) # If necessary, multiply the transformed term by a stabilizer to # cancel out Jordan-Wigner strings if len(indices) == 2: # Term is quadratic i, j = indices[0], indices[1] if (i, j) in vert_edges or (j, i) in vert_edges: # Term corresponds to a vertical edge, so we need to multiply # by a stabilizer top = min(i, j) bot = max(i, j) # Get the indices of the corresponding auxiliary qubits top_aux = expand_aux_index(top) bot_aux = expand_aux_index(bot) # Get the column that this edge is on col, row = snake_index_to_coordinates(top, x_dimension, y_dimension) # Multiply by a stabilizer. If the column is even, the # stabilizer corresponds to an edge that points down; # otherwise, the edge points up if col % 2 == 0: transformed_term *= stabilizer(top_aux, bot_aux) else: transformed_term *= stabilizer(bot_aux, top_aux) # Update the auxiliary Hamiltonian coefficient aux_ham_coefficient += abs(coefficient) # Transform the term to a QubitOperator and add it to the operator transformed_operator += jordan_wigner(transformed_term) # Add the auxiliary Hamiltonian if requested and compute the # resulting energy shift if add_auxiliary_hamiltonian: # Construct the auxiliary Hamiltonian graph aux_ham_graph = auxiliary_graph_2d_square(x_dimension, y_dimension) # Construct the auxiliary Hamiltonian aux_ham = FermionOperator() for i, j in aux_ham_graph.edges(): aux_ham -= stabilizer_local_2d_square(i, j, x_dimension, y_dimension) # Add an identity term to ensure that the auxiliary Hamiltonian # has ground energy equal to zero aux_ham += FermionOperator((), aux_ham_graph.size()) # Scale the auxiliary Hamiltonian aux_ham *= aux_ham_coefficient # Add it to the operator transformed_operator += jordan_wigner(aux_ham) return transformed_operator
active_space_start = 1 active_space_stop = 3 geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414)), ('H', (0., 0., 3.3))] # Generate and populate instance of MolecularData. molecule = MolecularData(geometry, basis, spin, description="h3") molecule = run_pyscf(molecule, run_scf=run_scf, run_mp2=run_mp2, run_cisd=run_cisd, run_ccsd=run_ccsd, run_fci=run_fci) # Use a Jordan-Wigner encoding, and compress to remove 0 imaginary components qubit_hamiltonian = jordan_wigner(molecule.get_molecular_hamiltonian()) qubit_hamiltonian.compress() pauli_hamiltonian = qubitop_to_pyquilpauli(qubit_hamiltonian) vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # angle = 2.0 # result = vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm) # print(result) angle_range = np.linspace(0.0, 2 * np.pi, 20) data = [ vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm) for angle in angle_range ]
def __init__(self, qc: Union[QuantumComputer, None] = None, hamiltonian: Union[PauliSum, List[PauliTerm], None] = None, molecule: MolecularData = None, method: str = 'Numpy', strategy: str = 'UCCSD', optimizer: str = 'BFGS', maxiter: int = 100000000, shotN: int = 10000, active_reset: bool = True, tomography: bool = False, verbose: bool = False, parametric: bool = False, custom_qubits=None): """ VQE experiment class. Initialize an instance of this class to prepare a VQE experiment. One may instantiate this class either based on an OpenFermion MolecularData object (containing a chemistry problem Hamiltonian) or manually suggest a Hamiltonian. The VQE can run circuits on different virtual or real backends: currently, we support the Rigetti QPU backend, locally running QVM, a WavefunctionSimulator, a NumpyWavefunctionSimulator, a PyQVM. Alternatively, one may run the VQE ansatz unitary directly (not decomposed as a circuit) via direct exponentiation of the unitary ansatz, with the 'linalg' method. The different backends do not all support parametric gates (yet), and the user can specify whether or not to use it. Currently, we support two built-in ansatz strategies and the option of setting your own ansatz circuit. The built-in UCCSD and HF strategies are based on data from MolecularData object and thus require one. For finding the groundstate of a custom Hamiltonian, it is required to manually set an ansatz strategy. Currently, the only classical optimizer for the VQE is the scipy.optimize.minimize module. This may be straightforwardly extended in future releases, contributions are welcome. This class can be initialized with any algorithm in the scipy class, and the max number of iterations can be specified. For some QuantumComputer objects, the qubit lattice is not numbered 0..N-1 but has architecture-specific logical labels. These need to be manually read from the lattice topology and specified in the list custom_qubits. On the physical hardware QPU, actively resetting the qubits is supported to speed up the repetition time of VQE. To debug and during development, set verbose=True to print output details to the console. :param [QuantumComputer(),None] qc: object :param [PauliSum, list(PauliTerm)] hamiltonian: Hamiltonian which one would like to simulate :param MolecularData molecule: OpenFermion Molecule data object. If this is given, the VQE module assumes a chemistry experiment using OpenFermion :param str method: string describing the Backend solver method. current options: {Numpy, WFS, linalg, QC} :param str strategy: string describing circuit VQE strategy. current options: {UCCSD, HF, custom_program} :param str optimizer: classical optimization algorithm, choose from scipy.optimize.minimize options :param int maxiter: max number of iterations :param int shotN: number of shots in the Tomography experiments :param bool active_reset: whether or not to actively reset the qubits (see ) :param bool tomography: set to False for access to full wavefunction, set to True for just sampling from it :param bool verbose: set to True for verbose output to the console, for all methods in this class :param bool parametric: set to True to use parametric gate compilation, False to compile a new circuit for every iteration :param list() custom_qubits: list of qubits, i.e. [7,0,1,2] ordering the qubit IDs as they appear on the QPU lattice of the QuantumComputer() object. """ if isinstance(hamiltonian, PauliSum): if molecule is not None: raise TypeError('Please supply either a Hamiltonian object or a Molecule object, but not both.') # Hamiltonian as a PauliSum, extracted to give a list instead self.pauli_list = hamiltonian.terms self.n_qubits = self.get_qubit_req() # assumes 0-(N-1) ordering and every pauli index is in use elif isinstance(hamiltonian, List): if molecule is not None: raise TypeError('Please supply either a Hamiltonian object or a Molecule object, but not both.') if len(hamiltonian) > 0: if all([isinstance(term, PauliTerm) for term in hamiltonian]): self.pauli_list = hamiltonian self.n_qubits = self.get_qubit_req() else: raise TypeError('Hamiltonian as a list must contain only PauliTerm objects') else: print('Warning, empty hamiltonian passed, assuming identity Hamiltonian = 1') self.pauli_list = [ID()] # this is allowed in principle, but won't make a lot of sense to use. elif hamiltonian is None: if molecule is None: raise TypeError('either feed a MolecularData object or a PyQuil Hamiltonian to this class') else: self.H = normal_ordered(get_fermion_operator(molecule.get_molecular_hamiltonian())) # store Fermionic # Hamiltonian in FermionOperator() instance self.qubitop = jordan_wigner(self.H) # Apply jordan_wigner transformation and store self.n_qubits = 2 * molecule.n_orbitals self.pauli_list = qubitop_to_pyquilpauli(self.qubitop).terms else: raise TypeError('hamiltonian must be a PauliSum or list of PauliTerms') # abstract QC. can refer to a qvm or qpu. QC architecture and available gates decide the compilation of the # programs! if isinstance(qc, QuantumComputer): self.qc = qc elif qc is None: self.qc = None else: raise TypeError('qc must be a QuantumComputer object. If you do not use a QC backend, omit, or supply ' 'qc=None') # number of shots in a tomography experiment if isinstance(shotN, int): self.shotN = shotN elif isinstance(shotN, float): self.shotN = int(shotN) else: raise TypeError('shotN must be an integer or float') # simulation method. Choose from methodoptions = ['WFS', 'linalg', 'QC', 'Numpy'] if method in methodoptions: self.method = method else: raise ValueError('choose a method from the following list: ' + str(methodoptions) + '. If a QPU, QVM or PyQVM is passed to qc, select QC.') # circuit strategy. choose from UCCSD, HF, custom_program strategyoptions = ['UCCSD', 'HF', 'custom_program'] if strategy in strategyoptions: if (strategy in ['UCCSD', 'HF']) and molecule is None: raise ValueError('Strategy selected, UCCSD or HF, requires a MolecularData object from PySCF as input.') self.strategy = strategy else: raise ValueError('choose a circuit strategy from the following list: ' + str(strategyoptions)) # classical optimizer classical_options = ['Nelder-Mead', 'Powell', 'CG', 'BFGS', 'Newton-CG', 'L-BFGS-B ', 'TNC', 'COBYLA', 'SLSQP', 'trust-constr', 'dogleg', 'trust-ncg', 'trust-exact', 'trust-krylov'] if optimizer not in classical_options: raise ValueError('choose a classical optimizer from the following list: ' + str(classical_options)) else: self.optimizer = optimizer # store the optimizer historical values self.history = [] # chemistry files. must be properly formatted in order to use a UCCSD ansatz (see MolecularData) self.molecule = molecule # whether or not the qubits should be actively reset. False will make the hardware wait for 3 coherence lengths # to go back to |0> self.active_reset = active_reset # max number of iterations for the classical optimizer self.maxiter = maxiter # vqe results, stores output of scipy.optimize.minimize, a OptimizeResult object. initialize to None self.res = None # list of grouped experiments (only relevant to tomography) self.experiment_list = [] # whether to print debugging data to console self.verbose = verbose # real QPU has a custom qubit labeling self.custom_qubits = custom_qubits # i'th function call self.it_num = 0 # whether to perform parametric method self.parametric_way = parametric # whether to do tomography or just calculate the wavefunction self.tomography = tomography # initialize offset for pauli terms with identity. this serves to avoid doing simulation for the identity # operator, which always yields unity times the coefficient due to wavefunction normalization. self.offset = 0 # set empty circuit unitary. This is used for the direct linear algebraic methods. self.circuit_unitary = None if strategy not in ['UCCSD', 'HF', 'custom_program']: raise ValueError('please select a strategy from UCCSD, HF, custom_program or modify this class with your ' 'own options') if strategy == 'UCCSD': # load UCCSD initial amps from the CCSD amps in the MolecularData() object amps = uccsd_singlet_get_packed_amplitudes(self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps, n_qubits=self.molecule.n_orbitals * 2, n_electrons=self.molecule.n_electrons) self.initial_packed_amps = amps else: # allocate empty initial angles for the circuit. modify later. self.initial_packed_amps = [] if (strategy is 'UCCSD') and (method is not 'linalg'): # UCCSD circuit strategy preparations self.ref_state = ref_state_preparation_circuit(molecule, ref_type='HF', cq=self.custom_qubits) if self.parametric_way: # in the parametric_way, the circuit is built with free parameters self.ansatz = uccsd_ansatz_circuit_parametric(self.molecule.n_orbitals, self.molecule.n_electrons, cq=self.custom_qubits) else: # in the non-parametric_way, the circuit has hard-coded angles for the gates. self.ansatz = uccsd_ansatz_circuit(self.initial_packed_amps, self.molecule.n_orbitals, self.molecule.n_electrons, cq=self.custom_qubits) elif strategy is 'HF': self.ref_state = ref_state_preparation_circuit(self.molecule, ref_type='HF', cq=self.custom_qubits) self.ansatz = Program() elif strategy is 'custom_program': self.parametric_way = True self.ref_state = Program() self.ansatz = Program() # prepare tomography experiment if necessary if self.tomography: if self.method == 'linalg': raise NotImplementedError('Tomography is not yet implemented for the linalg method.') self.compile_tomo_expts() else: # avoid having to re-calculate the PauliSum object each time, store it. self.pauli_sum = PauliSum(self.pauli_list) # perform miscellaneous method-specific preparations if self.method == 'QC': if qc is None: raise ValueError('Method is QC, please supply a valid QuantumComputer() object to the qc variable.') elif self.method == 'WFS': if (self.qc is not None) or (self.custom_qubits is not None): raise ValueError('The WFS method is not intended to be used with a custom qubit lattice or QuantumComputer object.') elif self.method == 'Numpy': if self.parametric_way: raise ValueError('NumpyWavefunctionSimulator() backend does not yet support parametric programs.') if (self.qc is not None) or (self.custom_qubits is not None): raise ValueError('NumpyWavefunctionSimulator() backend is not intended to be used with a ' 'QuantumComputer() object or custom lattice. Consider using PyQVM instead') elif self.method == 'linalg': if molecule is not None: # sparse initial state vector from the MolecularData() object self.initial_psi = jw_hartree_fock_state(self.molecule.n_electrons, 2*self.molecule.n_orbitals) # sparse operator from the MolecularData() object self.hamiltonian_matrix = get_sparse_operator(self.H, n_qubits=self.n_qubits) else: self.hamiltonian_matrix = get_sparse_operator(pyquilpauli_to_qubitop(PauliSum(self.pauli_list))) self.initial_psi = None print('Please supply VQE initial state with method VQEexperiment().set_initial_state()') else: raise ValueError('unknown method: please choose from method = {linalg, WFS, tomography} for direct linear ' 'algebra, pyquil WavefunctionSimulator, or doing Tomography, respectively')
def setUp(self): self.fermion_term = FermionOperator('1^ 2^ 3 4', -3.17) self.fermion_operator = self.fermion_term + hermitian_conjugated( self.fermion_term) self.qubit_operator = jordan_wigner(self.fermion_operator)
def test_function_errors(self): """Test error of main function.""" hamiltonian, spectrum = lih_hamiltonian() qubit_hamiltonian = jordan_wigner(hamiltonian) stab1 = QubitOperator('Z0 Z2', -1.0) stab2 = QubitOperator('Z1 Z3', -1.0) with self.assertRaises(TypeError): reduce_number_of_terms(operator=1, stabilizers=stab1 + stab2) with self.assertRaises(TypeError): reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=1) with self.assertRaises(TypeError): reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=stab1 + stab2, manual_input=True, fixed_positions=None) with self.assertRaises(StabilizerError): reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=stab1 + stab2, manual_input=True, fixed_positions=[1]) with self.assertRaises(StabilizerError): reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=stab1 + stab2, manual_input=True, fixed_positions=[1, 1]) with self.assertRaises(StabilizerError): # Check Identity as stabilizer error. reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=(stab1 + QubitOperator(' ', 1.0))) with self.assertRaises(StabilizerError): # Check complex coefficient stabilizer error. reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=(stab1 + QubitOperator('Z0', 1.0j))) with self.assertRaises(StabilizerError): # Check linearly-dependent stabilizer error. reduce_number_of_terms( operator=qubit_hamiltonian, stabilizers=(stab1 + QubitOperator('Z0 Z1 Z2 Z3', 1.0) + stab2)) with self.assertRaises(StabilizerError): # Check anti-commuting stabilizer error. reduce_number_of_terms(operator=qubit_hamiltonian, stabilizers=(QubitOperator('X0', 1.0) + QubitOperator('Y0', 1.0))) with self.assertRaises(StabilizerError): # Check linearly-dependent stabilizer error. _reduce_terms( terms=qubit_hamiltonian, stabilizer_list=list(stab1 + QubitOperator('Z0 Z1 Z2 Z3', 1.0) + stab2), manual_input=False, fixed_positions=[]) with self.assertRaises(StabilizerError): # Check complex coefficient stabilizer error. _reduce_terms(terms=qubit_hamiltonian, stabilizer_list=list(stab1 + QubitOperator('Z0', 1.0j)), manual_input=False, fixed_positions=[]) with self.assertRaises(StabilizerError): # Check linearly-dependent stabilizer error. par_qop = QubitOperator('Z0 Z1 Z2 Z3', 1.0) _reduce_terms_keep_length(terms=qubit_hamiltonian, stabilizer_list=[stab1, par_qop, stab2], manual_input=False, fixed_positions=[]) with self.assertRaises(StabilizerError): # Check complex coefficient stabilizer error. aux_qop = QubitOperator('Z0', 1.0j) _reduce_terms_keep_length(terms=qubit_hamiltonian, stabilizer_list=[stab1, aux_qop], manual_input=False, fixed_positions=[]) with self.assertRaises(StabilizerError): # Test check_commuting_stabilizer function # Requires a list of QubitOperators one of which # has an imaginary term. check_commuting_stabilizers(stabilizer_list=[ QubitOperator('Z0 Z1', 1.0), QubitOperator('X0', 1j) ], msg='This test fails.') with self.assertRaises(StabilizerError): # Test check_stabilizer_linearity function. # Requires a list of QUbitOperators one of which is # the identity. check_stabilizer_linearity( [QubitOperator('Z0 Z1', 1.0), QubitOperator(' ', 1.0)], msg='This test fails.')
def test_reverse_jordan_wigner(self): transmed_operator = reverse_jordan_wigner(self.qubit_operator) retransmed_operator = jordan_wigner(transmed_operator) self.assertTrue(self.qubit_operator == retransmed_operator)
def test_8mode_ffft_with_external_swaps_equal_expectation_values(self): n_qubits = 8 grid = Grid(dimensions=1, length=n_qubits, scale=1.0) dual_basis = jellium_model(grid, spinless=True, plane_wave=False) ffft_result = normal_ordered(dual_basis) # Swap 01234567 to 45670123 for fourier_transform. Additionally, # the FFFT's ordering is 04261537, so swap 04261537 to 01234567, # and then 01234567 to 45670123. swap_mode_list = ([1, 3, 5, 2, 4, 1, 3, 5] + [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3]) for mode in swap_mode_list: ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode) ffft_result = normal_ordered(ffft_result) ffft_result = fourier_transform(ffft_result, grid, spinless=True) ffft_result = normal_ordered(ffft_result) # After the FFFT, swap 45670123 -> 01234567. swap_mode_list = [3, 2, 4, 1, 3, 5, 0, 2, 4, 6, 1, 3, 5, 2, 4, 3] for mode in swap_mode_list: ffft_result = swap_adjacent_fermionic_modes(ffft_result, mode) ffft_result = normal_ordered(ffft_result) jw_dual_basis = jordan_wigner(dual_basis) jw_plane_wave = jordan_wigner(ffft_result) # Do plane wave and dual basis calculations simultaneously. pw_engine = MainEngine() pw_wavefunction = pw_engine.allocate_qureg(n_qubits) db_engine = MainEngine() db_wavefunction = db_engine.allocate_qureg(n_qubits) pw_engine.flush() db_engine.flush() # Choose random state. state = numpy.zeros(2 ** n_qubits, dtype=complex) for i in range(len(state)): state[i] = (random.random() * numpy.exp(1j * 2 * numpy.pi * random.random())) state /= numpy.linalg.norm(state) # Put randomly chosen state in the registers. pw_engine.backend.set_wavefunction(state, pw_wavefunction) db_engine.backend.set_wavefunction(state, db_wavefunction) prepare_logical_state(pw_wavefunction, i) prepare_logical_state(db_wavefunction, i) All(H) | [pw_wavefunction[1], pw_wavefunction[3]] All(H) | [db_wavefunction[1], db_wavefunction[3]] ffft(db_engine, db_wavefunction, n_qubits) Ph(3 * numpy.pi / 4) | db_wavefunction[0] # Flush the engine and compute expectation values and eigenvalues. pw_engine.flush() db_engine.flush() plane_wave_expectation_value = ( pw_engine.backend.get_expectation_value( jw_dual_basis, pw_wavefunction)) dual_basis_expectation_value = ( db_engine.backend.get_expectation_value( jw_plane_wave, db_wavefunction)) All(Measure) | pw_wavefunction All(Measure) | db_wavefunction self.assertAlmostEqual(plane_wave_expectation_value, dual_basis_expectation_value)
def test_y(self): pauli_y = QubitOperator(((2, 'Y'),)) transmed_y = reverse_jordan_wigner(pauli_y) retransmed_y = jordan_wigner(transmed_y) self.assertTrue(pauli_y == retransmed_y)
def __init__(self, ansatz, molecule, mean_field=None, backend_options={"backend": "wavefunction_simulator"}): """Initialize the settings for simulation. If the mean field is not provided it is automatically calculated. Args: ansatz (OpenFermionParametricSolver.Ansatze): Ansatz for the quantum solver. molecule (pyscf.gto.Mole): The molecule to simulate. mean_field (pyscf.scf.RHF): The mean field of the molecule. """ # Check the ansatz assert (isinstance(ansatz, RigettiParametricSolver.Ansatze)) self.ansatz = ansatz # Calculate the mean field if the user has not already done it. if not mean_field: mean_field = scf.RHF(molecule) mean_field.verbose = 0 mean_field.scf() if not mean_field.converged: orb_temp = mean_field.mo_coeff occ_temp = mean_field.mo_occ nr = scf.newton(mean_field) energy = nr.kernel(orb_temp, occ_temp) mean_field = nr # Check the convergence of the mean field if not mean_field.converged: warnings.warn( "RigettiParametricSolver simulating with mean field not converged.", RuntimeWarning) self.molecule = molecule self.mean_field = mean_field # Initialize the amplitudes (parameters to be optimized) self.optimized_amplitudes = [] # Set the parameters for Openfermion self.of_mole = self._build_of_molecule(molecule, mean_field) # Set the fermionic Hamiltonian self.f_hamiltonian = self.of_mole.get_molecular_hamiltonian() # Transform the fermionic Hamiltonian into qubit Hamiltonian self.qubit_hamiltonian = jordan_wigner(self.f_hamiltonian) self.qubit_hamiltonian.compress() # Also stores the Rigetti/Forest qubit Hamiltonian self.forest_qubit_hamiltonian = qubitop_to_pyquilpauli( self.qubit_hamiltonian) # Set the dimension of the amplitudes if ansatz == RigettiParametricSolver.Ansatze.UCCSD: no_occupied = int(np.ceil(molecule.nelectron / 2)) no_virtual = len(mean_field.mo_energy) - no_occupied no_t1 = no_occupied * no_virtual no_t2 = no_t1 * (no_t1 + 1) / 2 self.amplitude_dimension = int(no_t1 + no_t2) # Set the number of qubits self.n_qubits = self.of_mole.n_qubits # Instantiate backend for computation self.backend_options = dict() if ("backend" in backend_options) and (backend_options["backend"] != "wavefunction_simulator"): self.backend_options["backend"] = get_qc( backend_options["backend"]) else: self.backend_options["backend"] = WavefunctionSimulator() self.backend_options["n_shots"] = backend_options["n_shots"] if ( "n_shots" in backend_options) else 1000