'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)
예제 #4
0
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))
예제 #5
0
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)
예제 #7
0
 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)))
예제 #8
0
 def test_term(self):
     transmed_term = reverse_jordan_wigner(self.term)
     retransmed_term = jordan_wigner(transmed_term)
     self.assertTrue(self.term == retransmed_term)
예제 #9
0
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)
예제 #10
0
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()
예제 #11
0
 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)
예제 #12
0
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))
예제 #13
0
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)
예제 #14
0
파일: pec_JF.py 프로젝트: KKeita27/jastrow
    # ハミルトニアンを行列に変換
    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
예제 #15
0
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)
예제 #16
0
    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
예제 #17
0
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)
예제 #18
0
    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)
예제 #19
0
    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)
예제 #20
0
    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)
예제 #21
0
    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.)
예제 #22
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
예제 #23
0
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
]
예제 #24
0
    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')
예제 #25
0
 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)
예제 #28
0
    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)
예제 #30
0
    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