Пример #1
0
def test_fci_energy():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    np_ham = of.get_number_preserving_sparse_operator(
        of.get_fermion_operator(reduced_ham),
        molecule.n_qubits,
        num_electrons=molecule.n_electrons,
        spin_preserving=True)

    w, _ = np.linalg.eigh(np_ham.toarray())
    assert np.isclose(molecule.fci_energy, w[0])

    filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    np_ham = of.get_number_preserving_sparse_operator(
        of.get_fermion_operator(reduced_ham),
        molecule.n_qubits,
        num_electrons=molecule.n_electrons,
        spin_preserving=True)

    w, _ = np.linalg.eigh(np_ham.toarray())
    assert np.isclose(molecule.fci_energy, w[0])
Пример #2
0
def test_mrd_return_type():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)

    assert isinstance(reduced_ham, InteractionOperator)
Пример #3
0
def decompose_hamiltonian(mol_name,
                          hf_data,
                          mapping="jordan_wigner",
                          docc_mo_indices=None,
                          active_mo_indices=None):
    r"""Decomposes the electronic Hamiltonian into a linear combination of Pauli operators using
    OpenFermion tools.

    **Example usage:**

    >>> decompose_hamiltonian('h2', './pyscf/sto-3g/', mapping='bravyi_kitaev')
    (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] +
    (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] +
    (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] +
    (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] +
    (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] +
    (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] +
    (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] +
    (-0.2427428049645989+0j) [Z2]

    Args:
        mol_name (str): name of the molecule
        hf_data (str): path to the directory containing the file with the Hartree-Fock
            electronic structure
        mapping (str): optional argument to specify the fermion-to-qubit mapping
            Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``
        docc_mo_indices (list): indices of doubly-occupied molecular orbitals, i.e.,
            the orbitals that are not correlated in the many-body wave function
        active_mo_indices (list): indices of active molecular orbitals, i.e., the orbitals used to
            build the correlated many-body wave function

    Returns:
        transformed_operator: instance of the QubitOperator class representing the electronic
        Hamiltonian
    """

    # loading HF data from a hdf5 file
    molecule = MolecularData(
        filename=os.path.join(hf_data.strip(), mol_name.strip()))

    # getting the terms entering the second-quantized Hamiltonian
    terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=docc_mo_indices, active_indices=active_mo_indices)

    # generating the fermionic Hamiltonian
    fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian)

    mapping = mapping.strip().lower()

    if mapping 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))

    # fermionic-to-qubit transformation of the Hamiltonian
    if mapping == "bravyi_kitaev":
        return bravyi_kitaev(fermionic_hamiltonian)

    return jordan_wigner(fermionic_hamiltonian)
Пример #4
0
def get_qubit_hamiltonian(mol,
                          geometry,
                          basis,
                          charge=0,
                          multiplicity=1,
                          qubit_transf='bk'):
    '''
    Generating qubit hamiltonina of the given molecules with specified geometry, basis sets, charge and multiplicity
    Give its qubit form using specified transformation
    '''
    g = get_molecular_data(mol, geometry)
    mol = MolecularData(g, basis, multiplicity, charge)
    mol = run_pyscf(mol)

    ham = mol.get_molecular_hamiltonian()
    hamf = get_fermion_operator(ham)

    if qubit_transf == 'bk':
        hamq = bravyi_kitaev(hamf)
    elif qubit_transf == 'jw':
        hamq = jordan_wigner(hamf)
    else:
        raise (ValueError(qubit_transf, 'Unknown transformation specified'))

    return hamq
Пример #5
0
def system_h4():
    print('Running System Setup H4')
    basis = 'sto-3g'
    multiplicity = 1
    charge = 0
    r = 0.75
    geometry = [('H', [0.0, 0.0, 0.0]), ('H', [0, 0, r]), ('H', [0, 0, 2 * r]),
                ('H', [0, 0, 3 * r])]
    molecule = MolecularData(geometry, basis, multiplicity, charge)
    # Run Psi4.
    molecule = run_psi4(molecule,
                        run_scf=True,
                        run_mp2=False,
                        run_cisd=False,
                        run_ccsd=False,
                        run_fci=True,
                        delete_input=False)

    op_mat = of.get_sparse_operator(
        molecule.get_molecular_hamiltonian()).toarray()
    w, v = np.linalg.eigh(op_mat)
    n_density = v[:, [0]] @ v[:, [0]].conj().T
    rdm_generator = AntiSymmOrbitalDensity(n_density, molecule.n_qubits)
    transform = jordan_wigner
    return n_density, rdm_generator, transform, molecule
def lih_hamiltonian():
    """
    Generate test Hamiltonian from LiH.

    Args:
        None

    Return:

        hamiltonian: FermionicOperator

        spectrum: List of energies.
    """
    geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))]
    active_space_start = 1
    active_space_stop = 3
    molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45")
    molecule.load()

    molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=range(active_space_start),
        active_indices=range(active_space_start, active_space_stop))

    hamiltonian = get_fermion_operator(molecular_hamiltonian)
    spectrum = eigenspectrum(hamiltonian)

    return hamiltonian, spectrum
Пример #7
0
    def test_consistency(self):
        """Test consistency with JW for FermionOperators."""
        # Random interaction operator
        n_qubits = 5
        iop = random_interaction_operator(n_qubits, real=False)
        op1 = jordan_wigner(iop)
        op2 = jordan_wigner(get_fermion_operator(iop))

        self.assertEqual(op1, op2)

        # Interaction operator from molecule
        geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))]
        basis = 'sto-3g'
        multiplicity = 1

        filename = os.path.join(DATA_DIRECTORY, 'H1-Li1_sto-3g_singlet_1.45')
        molecule = MolecularData(geometry,
                                 basis,
                                 multiplicity,
                                 filename=filename)
        molecule.load()

        iop = molecule.get_molecular_hamiltonian()
        op1 = jordan_wigner(iop)
        op2 = jordan_wigner(get_fermion_operator(iop))

        self.assertEqual(op1, op2)
def exp_hamiltoniantrot_H2(time, atomic_distance, trotter_order):
    """
    Here we are using some packages related to quantum chemistry to obtain
    hamiltonian of H2 molecule. The we are translating this hamiltonian into
    sequence of pyquil gates. And finally, we are trotterazing the exponent
    of the hamiltonian (exp(-iHt)) of our system and returning the obtained
    pyquil program

    :param time: is t in the exp(iHt). Note the + sign.
    :param atomic_distance: the distance between to H atoms in H2 molecule

    :return: program of the Trotter-Suzuki decomposition of
             hamiltonian exp(iHt) for H2 molecule
    """

    geometry = [['H', [0, 0, 0]],
                ['H', [0, 0, atomic_distance]]]  # H--H distance = 0.74angstrom
    basis = 'sto-3g'
    multiplicity = 1  # (2S+1)
    charge = 0
    h2_molecule = MolecularData(geometry, basis, multiplicity, charge)
    h2_molecule = run_pyscf(h2_molecule)

    h2_qubit_hamiltonian = jordan_wigner(get_fermion_operator(h2_molecule.get_molecular_hamiltonian()))
    pyquil_h2_qubit_hamiltonian = qubitop_to_pyquilpauli(h2_qubit_hamiltonian)

    return trotterization(pyquil_h2_qubit_hamiltonian, float(time), trotter_order)
Пример #9
0
def test_erpa_eom_ham_h2():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    rha_fermion = of.get_fermion_operator(reduced_ham)
    permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor)
    opdm = np.diag([1] * molecule.n_electrons + [0] *
                   (molecule.n_qubits - molecule.n_electrons))
    tpdm = 2 * of.wedge(opdm, opdm, (1, 1), (1, 1))
    rdms = of.InteractionRDM(opdm, tpdm)
    dim = reduced_ham.one_body_tensor.shape[0] // 2
    full_basis = {}  # erpa basis.  A, B basis in RPA language
    cnt = 0
    for p, q in product(range(dim), repeat=2):
        if p < q:
            full_basis[(p, q)] = cnt
            full_basis[(q, p)] = cnt + dim * (dim - 1) // 2
            cnt += 1
    for rkey in full_basis.keys():
        p, q = rkey
        for ckey in full_basis.keys():
            r, s = ckey
            for sigma, tau in product([0, 1], repeat=2):
                test = erpa_eom_hamiltonian(permuted_hijkl, tpdm,
                                            2 * q + sigma, 2 * p + sigma,
                                            2 * r + tau, 2 * s + tau).real
                qp_op = of.FermionOperator(
                    ((2 * q + sigma, 1), (2 * p + sigma, 0)))
                rs_op = of.FermionOperator(
                    ((2 * r + tau, 1), (2 * s + tau, 0)))
                erpa_op = of.normal_ordered(
                    of.commutator(qp_op, of.commutator(rha_fermion, rs_op)))
                true = rdms.expectation(of.get_interaction_operator(erpa_op))
                assert np.isclose(true, test)
Пример #10
0
def test_constant_one_body():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)

    assert np.isclose(reduced_ham.constant, molecule.nuclear_repulsion)
    assert np.allclose(reduced_ham.one_body_tensor, 0)
Пример #11
0
def decompose(hf_file, mapping="jordan_wigner", core=None, active=None):
    r"""Decomposes the molecular Hamiltonian into a linear combination of Pauli operators using
    OpenFermion tools.

    This function uses OpenFermion functions to build the second-quantized electronic Hamiltonian
    of the molecule and map it to the Pauli basis using the Jordan-Wigner or Bravyi-Kitaev
    transformation.

    Args:
        hf_file (str): absolute path to the hdf5-formatted file with the
            Hartree-Fock electronic structure
        mapping (str): Specifies the transformation to map the fermionic Hamiltonian to the
            Pauli basis. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``.
        core (list): indices of core orbitals, i.e., the orbitals that are
            not correlated in the many-body wave function
        active (list): indices of active orbitals, i.e., the orbitals used to
            build the correlated many-body wave function

    Returns:
        QubitOperator: an instance of OpenFermion's ``QubitOperator``

    **Example**

    >>> decompose('./pyscf/sto-3g/h2', mapping='bravyi_kitaev')
    (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] +
    (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] +
    (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] +
    (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] +
    (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] +
    (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] +
    (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] +
    (-0.2427428049645989+0j) [Z2]
    """

    # loading HF data from the hdf5 file
    molecule = MolecularData(filename=hf_file.strip())

    # getting the terms entering the second-quantized Hamiltonian
    terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=core, active_indices=active
    )

    # generating the fermionic Hamiltonian
    fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian)

    mapping = mapping.strip().lower()

    if mapping 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)
        )

    # fermionic-to-qubit transformation of the Hamiltonian
    if mapping == "bravyi_kitaev":
        return bravyi_kitaev(fermionic_hamiltonian)

    return jordan_wigner(fermionic_hamiltonian)
def run_simulation(bond_lengths):
    # Load saved file for H3.
    basis = 'sto-3g'
    spin = 2

    # Set Hamiltonian parameters.
    active_space_start = 1
    active_space_stop = 3

    # Set calculation parameters.
    run_scf = 1
    run_mp2 = 1
    run_cisd = 0
    run_ccsd = 0
    run_fci = 1
    delete_input = True
    delete_output = True

    # Begin Running Simulation, Convert distance_counter to angstroms
    geometry = [('H', (0., 0., bond_lengths[1][-1] * 0.529177249)),
                ('H', (0., 0., bond_lengths[2][-1] * 0.529177249)),
                ('H', (0., 0., bond_lengths[3][-1] * 0.529177249))]

    # 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
    molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=range(active_space_start),
        active_indices=range(active_space_start, active_space_stop))

    fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian)
    qubit_hamiltonian = jordan_wigner(fermion_hamiltonian)
    qubit_hamiltonian.compress()
    compiler_engine = uccsd_trotter_engine()
    initial_energy = energy_objective(opt_amplitudes)

    # Run VQE Optimization to find new CCSD parameters
    opt_result = minimize(energy_objective,
                          opt_amplitudes,
                          method="CG",
                          options={'disp': True})

    opt_energy, opt_amplitudes = opt_result.fun, opt_result.x

    return ({
        "Name": molecule.name,
        "UCCSD Energy": opt_energy,
        "FCI Energy": molecule.fci_energy
    })
Пример #13
0
    def test_one_body_square_decomposition(self):

        # Initialize H2 InteractionOperator.
        n_qubits = 4
        n_orbitals = 2
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(filename=filename)
        molecule_interaction = molecule.get_molecular_hamiltonian()
        fermion_operator = get_fermion_operator(molecule_interaction)

        two_body_coefficients = molecule_interaction.two_body_tensor

        # Decompose.
        eigenvalues, one_body_squares, one_body_correction, error = (
            low_rank_two_body_decomposition(two_body_coefficients))
        rank = eigenvalues.size
        for l in range(rank):
            one_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_squares[l, p, q]
                one_body_operator += FermionOperator(term, coefficient)
            one_body_squared = one_body_operator**2

            # Get the squared one-body operator via one-body decomposition.
            if abs(eigenvalues[l]) < 1e-6:
                with self.assertRaises(ValueError):
                    prepare_one_body_squared_evolution(one_body_squares[l])
                continue
            else:
                density_density_matrix, basis_transformation_matrix = (
                    prepare_one_body_squared_evolution(one_body_squares[l]))
            two_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (p, 0), (q, 1), (q, 0))
                coefficient = density_density_matrix[p, q]
                two_body_operator += FermionOperator(term, coefficient)

            # Confirm that the rotations diagonalize the one-body squares.
            hopefully_diagonal = basis_transformation_matrix.dot(
                numpy.dot(
                    one_body_squares[l],
                    numpy.transpose(
                        numpy.conjugate(basis_transformation_matrix))))
            diagonal = numpy.diag(hopefully_diagonal)
            difference = hopefully_diagonal - numpy.diag(diagonal)
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))
            density_density_alternative = numpy.outer(diagonal, diagonal)
            difference = density_density_alternative - density_density_matrix
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))

            # Test spectra.
            one_body_squared_spectrum = eigenspectrum(one_body_squared)
            two_body_spectrum = eigenspectrum(two_body_operator)
            difference = two_body_spectrum - one_body_squared_spectrum
            self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))
Пример #14
0
    def test_molecular_operator_consistency(self):

        # Initialize H2 InteractionOperator.
        n_qubits = 4
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(filename=filename)
        molecule_interaction = molecule.get_molecular_hamiltonian()
        molecule_operator = get_fermion_operator(molecule_interaction)

        constant = molecule_interaction.constant
        one_body_coefficients = molecule_interaction.one_body_tensor
        two_body_coefficients = molecule_interaction.two_body_tensor

        # Perform decomposition.
        eigenvalues, one_body_squares, one_body_corrections, trunc_error = (
            low_rank_two_body_decomposition(two_body_coefficients))
        self.assertAlmostEqual(trunc_error, 0.)

        # Build back operator constant and one-body components.
        decomposed_operator = FermionOperator((), constant)
        for p, q in itertools.product(range(n_qubits), repeat=2):
            term = ((p, 1), (q, 0))
            coefficient = (one_body_coefficients[p, q] +
                           one_body_corrections[p, q])
            decomposed_operator += FermionOperator(term, coefficient)

        # Build back two-body component.
        for l in range(one_body_squares.shape[0]):
            one_body_operator = FermionOperator()
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_squares[l, p, q]
                if abs(eigenvalues[l]) > 1e-6:
                    self.assertTrue(is_hermitian(one_body_squares[l]))
                one_body_operator += FermionOperator(term, coefficient)
            decomposed_operator += eigenvalues[l] * (one_body_operator ** 2)

        # Test for consistency.
        difference = normal_ordered(decomposed_operator - molecule_operator)
        self.assertAlmostEqual(0., difference.induced_norm())

        # Decompose with slightly negative operator that must use eigen
        molecule = MolecularData(filename=filename)
        molecule.two_body_integrals[0, 0, 0, 0] -= 1

        eigenvalues, one_body_squares, one_body_corrections, trunc_error = (
            low_rank_two_body_decomposition(two_body_coefficients))
        self.assertAlmostEqual(trunc_error, 0.)

        # Check for property errors
        with self.assertRaises(TypeError):
            eigenvalues, one_body_squares, _, trunc_error = (
                low_rank_two_body_decomposition(
                    two_body_coefficients + 0.01j,
                    truncation_threshold=1.,
                    final_rank=1))
def get_BK_ham(distance, basis="sto-3g", multiplicity=1, charge=1):
    geometry = [["He", [0, 0, 0]], ["H", [0, 0, distance]]]
    description = str(distance)
    molecule = MolecularData(geometry, basis, multiplicity, charge,
                             description)
    molecule = run_pyscf(molecule, run_fci=1)
    bk_hamiltonian = get_sparse_operator(
        bravyi_kitaev(
            get_fermion_operator(molecule.get_molecular_hamiltonian())))
    return bk_hamiltonian
Пример #16
0
def lih_hamiltonian():
    geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))]
    active_space_start = 1
    active_space_stop = 3
    molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45")
    molecule.load()
    molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=range(active_space_start),
        active_indices=range(active_space_start, active_space_stop))
    hamiltonian = get_fermion_operator(molecular_hamiltonian)
    ground_state_energy = eigenspectrum(hamiltonian)[0]
    return hamiltonian, ground_state_energy
Пример #17
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 = range(183)
    active_indices = range(183, 183+5)

    # 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)

    import pdb; pdb.set_trace()

    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'))

    return remove_complex(hamq)
class InteractionRDMTest(unittest.TestCase):
    def setUp(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()
        self.cisd_energy = self.molecule.cisd_energy
        self.rdm = self.molecule.get_molecular_rdm()
        self.hamiltonian = self.molecule.get_molecular_hamiltonian()

    def test_get_qubit_expectations(self):
        qubit_operator = jordan_wigner(self.hamiltonian)
        qubit_expectations = self.rdm.get_qubit_expectations(qubit_operator)

        test_energy = 0.0
        for qubit_term in qubit_expectations.terms:
            term_coefficient = qubit_operator.terms[qubit_term]
            test_energy += (term_coefficient *
                            qubit_expectations.terms[qubit_term])
        self.assertLess(abs(test_energy - self.cisd_energy), EQ_TOLERANCE)

    def test_get_qubit_expectations_nonmolecular_term(self):
        with self.assertRaises(InteractionRDMError):
            self.rdm.get_qubit_expectations(QubitOperator('X1 X2 X3 X4 Y6'))

    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_get_molecular_operator_expectation(self):
        expectation = self.rdm.expectation(self.hamiltonian)
        self.assertAlmostEqual(expectation, self.cisd_energy, places=7)

    def test_expectation_bad_type(self):
        with self.assertRaises(InteractionRDMError):
            self.rdm.expectation(12)

    def test_addition(self):
        rdm2 = self.rdm + self.rdm
        self.assertTrue(
            numpy.array_equal(rdm2.one_body_tensor,
                              rdm2.n_body_tensors[(1, 0)]))
        self.assertTrue(
            numpy.array_equal(rdm2.two_body_tensor,
                              rdm2.n_body_tensors[(1, 1, 0, 0)]))
Пример #19
0
def energy_levels(separation):
    g = [('H', (0., 0., 0.)), ('H', (0., 0., separation))]
    d = str(separation)
    m = MolecularData(g,
                      basis,
                      multiplicity,
                      description=d,
                      data_directory=data_directory)
    run_psi4(m, run_fci=True, delete_output=True)
    _, s = get_ground_state(
        get_sparse_operator(
            normal_ordered(
                get_fermion_operator(
                    m.get_molecular_hamiltonian(
                        active_indices=range(active_spatial_orbitals))))))
    h = normal_ordered(
        get_fermion_operator(
            m.get_molecular_hamiltonian(
                active_indices=range(active_spatial_orbitals +
                                     virtual_spatial_orbitals))))
    o = vqse_operators(s)
    return generalized_eigenvalues(h_matrix(h, o, s), s_matrix(o, s))
def LiH_sto3g():
    """ Generates the Hamiltonian for LiH in
        the STO-3G basis, at a distance of
        1.45 A.
    """
    geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))]
    molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45")
    molecule.load()
    molecular_hamiltonian = molecule.get_molecular_hamiltonian()
    hamiltonian = get_fermion_operator(molecular_hamiltonian)
    num_electrons = molecule.n_electrons
    num_orbitals = 2 * molecule.n_orbitals

    return hamiltonian, num_orbitals, num_electrons
Пример #21
0
def test_h2_rpa():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    hf_opdm = np.diag([1] * molecule.n_electrons + [0] *
                      (molecule.n_qubits - molecule.n_electrons))
    hf_tpdm = 2 * of.wedge(hf_opdm, hf_opdm, (1, 1), (1, 1))

    pos_spectrum, xy_eigvects, basis = singlet_erpa(
        hf_tpdm, reduced_ham.two_body_tensor)
    assert np.isclose(pos_spectrum, 0.92926444)  # pyscf-rpa value
    assert isinstance(xy_eigvects, np.ndarray)
    assert isinstance(basis, dict)
Пример #22
0
def load_and_transform(filename, orbitals, transform):
    # Load data
    print('--- loading molecule ---')
    molecule = MolecularData(filename=filename)

    print('filename: {}'.format(molecule.filename))
    #print('n_atoms: {}'.format(molecule.n_atoms))
    #print('n_electrons: {}'.format(molecule.n_electrons))
    #print('n_orbitals: {}'.format(molecule.n_orbitals))
    #print('Canonical Orbitals: {}'.format(molecule.canonical_orbitals))
    #print('n_qubits: {}'.format(molecule.n_qubits))

    # get the Hamiltonian for a specific choice of active space
    # set the Hamiltonian parameters
    occupied_orbitals, active_orbitals = orbitals

    molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=range(occupied_orbitals),
        active_indices=range(active_orbitals))

    # map the operator to fermions and then qubits
    fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian)

    # get interaction operator
    interaction_hamiltonian = get_interaction_operator(fermion_hamiltonian)

    if transform is 'JW':
        qubit_h = jordan_wigner(fermion_hamiltonian)
        qubit_h.compress()
    elif transform is 'BK':
        qubit_h = bravyi_kitaev(fermion_hamiltonian)
        qubit_h.compress()
    elif transform is 'BKSF':
        qubit_h = bravyi_kitaev_fast(interaction_hamiltonian)
        qubit_h.compress()
    elif transform is 'BKT':
        qubit_h = bravyi_kitaev_tree(fermion_hamiltonian)
        qubit_h.compress()
    elif transform is 'PC':
        qubit_h = binary_code_transform(fermion_hamiltonian,
                                        parity_code(2 * active_orbitals))
        qubit_h.compress()
    else:
        print('ERROR: Unrecognized qubit transformation: {}'.format(transform))
        sys.exit(2)

    return qubit_h
Пример #23
0
    def setUp(self):

        # Setup.
        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')
        molecule = MolecularData(
            geometry, basis, multiplicity, filename=filename)
        molecule.load()
        self.n_fermions = molecule.n_electrons
        self.n_orbitals = molecule.n_qubits

        # Get molecular Hamiltonian.
        self.molecular_hamiltonian = molecule.get_molecular_hamiltonian()
        self.fci_rdm = molecule.get_molecular_rdm(use_fci=1)
Пример #24
0
def expectation(p1, p2, p3, multi=1):
    """ 
    Return UCC expectation value for a specified geometry 
    * First runs a psi4 ccsd calculation to get single and double
      amplitudes to use as ansatz for UCC
    * Generates a Hamiltonian for the specified geometry
    * Obtains expectation value using VQE 
    """
    geometry = [['O', p1], ['H', p2], ['H', p3]]
    molecule = MolecularData(geometry,
                             basis='sto-3g',
                             multiplicity=multi,
                             description=str(round(rad, 2)) + "_" +
                             str(round(ang, 2)))
    # Run Psi4.
    molecule = run_psi4(molecule, run_ccsd=1, run_fci=1)
    # Print out some results of calculation.
    print('\nRAD: {}, ANG: {}\n'.format(rad, ang))
    print('FCI energy: {} Hartree.'.format(molecule.fci_energy))

    singles_initial = molecule.ccsd_single_amps.flatten()
    doubles_initial = molecule.ccsd_double_amps.flatten()
    amps = np.concatenate((singles_initial, doubles_initial), axis=0)

    print("Compiling the Hamiltonian...")
    hamiltonian = jordan_wigner(
        get_fermion_operator(molecule.get_molecular_hamiltonian()))
    hamiltonian.compress()
    hamiltonian = qubitop_to_pyquilpauli(hamiltonian)
    print("Hamiltonian complete")

    vqe = VQE(minimizer=minimize,
              minimizer_kwargs={
                  'method': 'nelder-mead',
                  'options': {
                      'fatol': 1.5e-3
                  }
              })
    result = vqe.expectation(ansatz(amps), hamiltonian, None, qvm)
    print("VQE Expectation Value: {} Hartree".format(result))

    return result
Пример #25
0
def FCI_get_Energy(bond_length):
    #求めるハミルトニアンのデータ
    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)

    #FCI計算
    molecule = run_psi4(molecule, run_scf=1, run_fci=1)
    eigenenergies, eigenvecs = eigs(jw_matrix)

    return molecule.fci_energy
Пример #26
0
def load_and_transform(filename, orbitals, transform):
    # Load data
    print('--- Loading molecule ---')
    molecule = MolecularData(filename=filename)
    molecule.load()

    print('filename: {}'.format(molecule.filename))
    print('n_atoms: {}'.format(molecule.n_atoms))
    print('n_electrons: {}'.format(molecule.n_electrons))
    print('n_orbitals: {}'.format(molecule.n_orbitals))
    #print('Canonical Orbitals: {}'.format(molecule.canonical_orbitals))
    print('n_qubits: {}'.format(molecule.n_qubits))

    # Get the Hamiltonian in an active space.
    # Set Hamiltonian parameters.
    occupied_orbitals, active_orbitals = orbitals

    molecular_hamiltonian = molecule.get_molecular_hamiltonian(
        occupied_indices=range(occupied_orbitals),
        active_indices=range(active_orbitals))

    # Map operator to fermions and qubits.
    fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian)
    #print('Fermionic Hamiltonian is:\n{}'.format(fermion_hamiltonian))

    if transform is 'JW':
        qubit_h = jordan_wigner(fermion_hamiltonian)
        qubit_h.compress()
        print('\nJordan-Wigner Hamiltonian:\n{}'.format(qubit_h))
    elif transform is 'BK':
        qubit_h = bravyi_kitaev(fermion_hamiltonian)
        qubit_h.compress()
        print('\nBravyi-Kitaev Hamiltonian is:\n{}'.format(qubit_h))
    else:
        print('ERROR: Unrecognized qubit transformation: {}'.format(transform))
        sys.exit(2)

    return qubit_h
Пример #27
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 = 100000,
            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.
        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
        :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')
        print(f"shots = {self.shotN}")

        # 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 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 = None

        # 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

        # 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 == 'UCCSD') and (method != '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 == 'HF':
            self.ref_state = ref_state_preparation_circuit(
                    self.molecule,
                    ref_type='HF',
                    cq=self.custom_qubits)
            self.ansatz = Program()
        elif strategy == 'custom_program':
            self.parametric_way = True
            self.ref_state = Program()
            self.ansatz = Program()

        if self.tomography:
            self.term_es = {}
            if self.method == 'linalg':
                raise NotImplementedError('Tomography is not'
                        ' yet implemented for the linalg method.')
        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')
Пример #28
0
    bond_length = bond_length_interval * float(point) + 0.2
    bond_lengths += [bond_length]
    geometry = [('H', (0., 0., 0.)), ('H', (0., 0., bond_length)), ('H', (0., 0., 3.3))]

    molecule = MolecularData(geometry, basis, spin, description=str(round(bond_length, 2)))

    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()

    pyquil_program = exponentiate(qubit_hamiltonian)
    qvm = WavefunctionSimulator()
    wf = qvm.wavefunction(pyquil_program)
    print('The {} has wavefunction amplitudes of'.format(molecule.name))
    print(wf.amplitudes)
Пример #29
0
    def test_rank_reduction(self):

        # Initialize H2.
        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')
        molecule = MolecularData(geometry,
                                 basis,
                                 multiplicity,
                                 filename=filename)
        molecule.load()

        # Get molecular Hamiltonian.
        molecular_hamiltonian = molecule.get_molecular_hamiltonian()

        # Get fermion Hamiltonian.
        fermion_hamiltonian = normal_ordered(
            get_fermion_operator(molecular_hamiltonian))

        # Get chemist tensor.
        constant, one_body_coefficients, chemist_tensor = (
            get_chemist_two_body_coefficients(fermion_hamiltonian))
        n_qubits = one_body_coefficients.shape[0]

        # Rank reduce with threshold.
        errors = []
        for truncation_threshold in [1., 0.1, 0.01, 0.001]:

            # Add back one-body terms and constant.
            decomposed_operator = FermionOperator((), constant)
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_coefficients[p, q]
                decomposed_operator += FermionOperator(term, coefficient)

            # Rank reduce.
            eigenvalues, one_body_squares, trunc_error = (
                low_rank_two_body_decomposition(
                    chemist_tensor, truncation_threshold=truncation_threshold))

            # Make sure error is below truncation specification.
            self.assertTrue(trunc_error < truncation_threshold)

            # Reassemble FermionOperator.
            l_max = eigenvalues.size
            for l in range(l_max):
                one_body_operator = FermionOperator()
                for p, q in itertools.product(range(n_qubits), repeat=2):
                    term = ((p, 1), (q, 0))
                    coefficient = one_body_squares[l, p, q]
                    one_body_operator += FermionOperator(term, coefficient)
                decomposed_operator += eigenvalues[l] * (one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator -
                                        fermion_hamiltonian)
            errors += [difference.induced_norm()]
            self.assertTrue(errors[-1] <= trunc_error
                            or abs(errors[-1] - trunc_error) < 1e-6)
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])

        # Rank reduce by setting final rank.
        errors = []
        for final_rank in [4, 6, 8, 10, 12]:

            # Add back one-body terms and constant.
            decomposed_operator = FermionOperator((), constant)
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_coefficients[p, q]
                decomposed_operator += FermionOperator(term, coefficient)

            # Rank reduce.
            eigenvalues, one_body_squares, trunc_error = (
                low_rank_two_body_decomposition(chemist_tensor,
                                                final_rank=final_rank))

            # Reassemble FermionOperator.
            l_max = eigenvalues.size
            for l in range(l_max):
                one_body_operator = FermionOperator()
                for p, q in itertools.product(range(n_qubits), repeat=2):
                    term = ((p, 1), (q, 0))
                    coefficient = one_body_squares[l, p, q]
                    one_body_operator += FermionOperator(term, coefficient)
                decomposed_operator += eigenvalues[l] * (one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator -
                                        fermion_hamiltonian)
            errors += [difference.induced_norm()]
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])
Пример #30
0
    def test_rank_reduction(self):

        # Initialize H2 InteractionOperator.
        n_qubits = 4
        n_orbitals = 2
        filename = os.path.join(THIS_DIRECTORY, 'data',
                                'H2_sto-3g_singlet_0.7414')
        molecule = MolecularData(filename=filename)
        molecule_interaction = molecule.get_molecular_hamiltonian()
        fermion_operator = get_fermion_operator(molecule_interaction)

        constant = molecule_interaction.constant
        two_body_coefficients = molecule_interaction.two_body_tensor

        # Rank reduce with threshold.
        errors = []
        for truncation_threshold in [1., 0.1, 0.01, 0.001]:

            # Decompose with threshold.
            (test_eigenvalues, one_body_squares, one_body_correction,
             trunc_error) = low_rank_two_body_decomposition(
                 two_body_coefficients,
                 truncation_threshold=truncation_threshold)

            # Make sure error is below truncation specification.
            self.assertTrue(trunc_error > 0.)
            self.assertTrue(trunc_error < truncation_threshold)
            self.assertTrue(len(test_eigenvalues) < n_orbitals**2)
            self.assertTrue(len(one_body_squares) == len(test_eigenvalues))

            # Build back operator constant and one-body components.
            decomposed_operator = FermionOperator((), constant)
            one_body_coefficients = (molecule_interaction.one_body_tensor +
                                     one_body_correction)
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_coefficients[p, q]
                decomposed_operator += FermionOperator(term, coefficient)

            # Build back two-body component.
            for l in range(len(test_eigenvalues)):
                one_body_operator = FermionOperator()
                for p, q in itertools.product(range(n_qubits), repeat=2):
                    term = ((p, 1), (q, 0))
                    coefficient = one_body_squares[l, p, q]
                    one_body_operator += FermionOperator(term, coefficient)
                decomposed_operator += (test_eigenvalues[l] *
                                        one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator - fermion_operator)
            errors += [difference.induced_norm()]
            self.assertTrue(errors[-1] <= trunc_error)
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])

        # Rank reduce by imposing final rank.
        errors = []
        for final_rank in [1, 2, 3, 4]:

            # Decompose with threshold.
            (test_eigenvalues, one_body_squares, one_body_correction,
             trunc_error) = low_rank_two_body_decomposition(
                 two_body_coefficients, final_rank=final_rank)

            # Make sure error is below truncation specification.
            self.assertTrue(len(test_eigenvalues) == final_rank)

            # Build back operator constant and one-body components.
            decomposed_operator = FermionOperator((), constant)
            one_body_coefficients = (molecule_interaction.one_body_tensor +
                                     one_body_correction)
            for p, q in itertools.product(range(n_qubits), repeat=2):
                term = ((p, 1), (q, 0))
                coefficient = one_body_coefficients[p, q]
                decomposed_operator += FermionOperator(term, coefficient)

            # Build back two-body component.
            for l in range(final_rank):
                one_body_operator = FermionOperator()
                for p, q in itertools.product(range(n_qubits), repeat=2):
                    term = ((p, 1), (q, 0))
                    coefficient = one_body_squares[l, p, q]
                    one_body_operator += FermionOperator(term, coefficient)
                decomposed_operator += (test_eigenvalues[l] *
                                        one_body_operator**2)

            # Test for consistency.
            difference = normal_ordered(decomposed_operator - fermion_operator)
            errors += [difference.induced_norm()]
        self.assertTrue(errors[3] <= errors[2] <= errors[1] <= errors[0])