示例#1
0
    def test_hf_state_plane_wave_basis_lowest_single_determinant_state(self):
        grid_length = 7
        dimension = 1
        spinless = True
        n_particles = 4
        length_scale = 2.0

        grid = Grid(dimension, grid_length, length_scale)
        hamiltonian = jellium_model(grid, spinless)
        hamiltonian_sparse = get_sparse_operator(hamiltonian)

        hf_state = hartree_fock_state_jellium(grid,
                                              n_particles,
                                              spinless,
                                              plane_wave=True)

        HF_energy = expectation(hamiltonian_sparse, hf_state)

        for occupied_orbitals in permutations([1] * n_particles + [0] *
                                              (grid_length - n_particles)):
            state_index = numpy.sum(2**numpy.array(occupied_orbitals))
            HF_competitor = numpy.zeros(2**grid_length)
            HF_competitor[state_index] = 1.0

            self.assertLessEqual(
                HF_energy, expectation(hamiltonian_sparse, HF_competitor))
示例#2
0
    def test_hf_state_energy_close_to_ground_energy_at_high_density(self):
        grid_length = 8
        dimension = 1
        spinless = True
        n_particles = grid_length**dimension // 2

        # High density -> small length_scale.
        length_scale = 0.25

        grid = Grid(dimension, grid_length, length_scale)
        hamiltonian = jellium_model(grid, spinless)
        hamiltonian_sparse = get_sparse_operator(hamiltonian)

        hf_state = hartree_fock_state_jellium(grid,
                                              n_particles,
                                              spinless,
                                              plane_wave=True)

        restricted_hamiltonian = jw_number_restrict_operator(
            hamiltonian_sparse, n_particles)

        E_g = get_ground_state(restricted_hamiltonian)[0]
        E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state)

        self.assertAlmostEqual(E_g, E_HF_plane_wave, places=5)
    def test_apply_constraints(self):

        # Get norm of original operator.
        original_norm = 0.
        for term, coefficient in self.fermion_hamiltonian.terms.items():
            if term != ():
                original_norm += abs(coefficient)

        # Get modified operator.
        modified_operator = apply_constraints(self.fermion_hamiltonian,
                                              self.n_fermions)
        modified_operator.compress()

        # Get norm of modified operator.
        modified_norm = 0.
        for term, coefficient in modified_operator.terms.items():
            if term != ():
                modified_norm += abs(coefficient)
        self.assertTrue(modified_norm < original_norm)

        # Map both to sparse matrix under Jordan-Wigner.
        sparse_original = get_sparse_operator(self.fermion_hamiltonian)
        sparse_modified = get_sparse_operator(modified_operator)

        # Check spectra.
        sparse_original = jw_number_restrict_operator(sparse_original,
                                                      self.n_fermions)
        sparse_modified = jw_number_restrict_operator(sparse_modified,
                                                      self.n_fermions)
        original_spectrum = sparse_eigenspectrum(sparse_original)
        modified_spectrum = sparse_eigenspectrum(sparse_modified)
        spectral_deviation = numpy.amax(
            numpy.absolute(original_spectrum - modified_spectrum))
        self.assertAlmostEqual(spectral_deviation, 0.)

        # Check expectation value.
        energy, wavefunction = get_ground_state(sparse_original)
        modified_energy = expectation(sparse_modified, wavefunction)
        self.assertAlmostEqual(modified_energy, energy)

        # Test consistency with cvxopt.
        scipy_operator = apply_constraints(self.fermion_hamiltonian,
                                           self.n_fermions,
                                           use_scipy=False)

        # Get norm.
        scipy_norm = 0.
        for term, coefficient in scipy_operator.terms.items():
            if term != ():
                scipy_norm += abs(coefficient)
        self.assertAlmostEqual(scipy_norm, modified_norm)
示例#4
0
    def test_hf_state_energy_same_in_plane_wave_and_dual_basis(self):
        grid_length = 4
        dimension = 1
        wigner_seitz_radius = 10.0
        spinless = False

        n_orbitals = grid_length**dimension
        if not spinless:
            n_orbitals *= 2
        n_particles = n_orbitals // 2

        length_scale = wigner_seitz_length_scale(wigner_seitz_radius,
                                                 n_particles, dimension)

        grid = Grid(dimension, grid_length, length_scale)
        hamiltonian = jellium_model(grid, spinless)
        hamiltonian_dual_basis = jellium_model(grid,
                                               spinless,
                                               plane_wave=False)

        # Get the Hamiltonians as sparse operators.
        hamiltonian_sparse = get_sparse_operator(hamiltonian)
        hamiltonian_dual_sparse = get_sparse_operator(hamiltonian_dual_basis)

        hf_state = hartree_fock_state_jellium(grid,
                                              n_particles,
                                              spinless,
                                              plane_wave=True)
        hf_state_dual = hartree_fock_state_jellium(grid,
                                                   n_particles,
                                                   spinless,
                                                   plane_wave=False)

        E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state)
        E_HF_dual = expectation(hamiltonian_dual_sparse, hf_state_dual)

        self.assertAlmostEqual(E_HF_dual, E_HF_plane_wave)
示例#5
0
def compute_energy_expectation(bond_length, state):
    """Computes the energy expectation given the bond length (to fetch the
    Hamiltonian) and state.

    Args:
    =====
    bond_length : float
        Bond length for fetching correct Hamiltonian
    state : numpy.ndarray
        State vector

    Returns: 
    ========
    energy : float
        Energy expectation value

    """
    energy = expectation(sparse_hamiltonians[dist_list.index(bond_length)],
                         state)
    return np.real(energy)
    def test_constraint_matrix(self):

        # Randomly project operator with constraints.
        numpy.random.seed(8)
        constraints = constraint_matrix(self.n_orbitals, self.n_fermions)
        n_constraints, n_terms = constraints.shape
        self.assertEqual(1 + self.n_orbitals**2 + self.n_orbitals**4, n_terms)
        random_weights = numpy.random.randn(n_constraints)
        vectorized_operator = operator_to_vector(self.fermion_hamiltonian)
        modification_vector = constraints.transpose() * random_weights
        new_operator_vector = vectorized_operator + modification_vector
        modified_operator = vector_to_operator(new_operator_vector,
                                               self.n_orbitals)

        # Map both to sparse matrix under Jordan-Wigner.
        sparse_original = get_sparse_operator(self.fermion_hamiltonian)
        sparse_modified = get_sparse_operator(modified_operator)

        # Check expectation value.
        energy, wavefunction = get_ground_state(sparse_original)
        modified_energy = expectation(sparse_modified, wavefunction)
        self.assertAlmostEqual(modified_energy, energy)
示例#7
0
    def objective_function(self, amps=None):
        """
        This function returns the Hamiltonian expectation value
        over the final circuit output state.
        If argument packed_amps is given,
        the circuit will run with those parameters.
        Otherwise, the initial angles will be used.

        :param [list(), numpy.ndarray] amps: list of circuit angles
                to run the objective function over.

        :return: energy estimate
        :rtype: float
        """

        E = 0
        t = time.time()
        if amps is None:
            packed_amps = self.initial_packed_amps
        elif isinstance(amps, np.ndarray):
            packed_amps = amps.tolist()
        elif isinstance(amps, list):
            packed_amps = amps
        else:
            raise TypeError('Please supply the circuit parameters'
                    ' as a list or np.ndarray')

        if self.tomography:
            if (not self.parametric_way) and (self.strategy == 'UCCSD'):
                # modify hard-coded type ansatz circuit based
                # on packed_amps angles
                self.ansatz = uccsd_ansatz_circuit(
                        packed_amps,
                        self.molecule.n_orbitals,
                        self.molecule.n_electrons,
                        cq=self.custom_qubits)
            if self.experiment_list is None or not self.parametric_way:
                self.compile_tomo_expts()
            self.run_experiments(packed_amps)
            for term in self.pauli_list:
                key = term.operations_as_set()
                if len(key) > 0:
                     E += term.coefficient*self.term_es[key]
                else:
                    E += term.coefficient

        elif self.method == 'WFS':
            # In the direct WFS method without tomography,
            # direct access to wavefunction is allowed and expectation
            # value is exact each run.
            if self.parametric_way:
                E += WavefunctionSimulator().expectation(
                        self.ref_state+self.ansatz,
                        self.pauli_sum,
                        {'theta': packed_amps}).real
                # attach parametric angles here
            else:
                if packed_amps is not None:
                    # modify hard-coded type ansatz circuit
                    # based on packed_amps angles
                    self.ansatz = uccsd_ansatz_circuit(
                            packed_amps,
                            self.molecule.n_orbitals,
                            self.molecule.n_electrons,
                            cq=self.custom_qubits)
                E += WavefunctionSimulator().expectation(
                        self.ref_state+self.ansatz,
                        self.pauli_sum).real
        elif self.method == 'Numpy':
            if self.parametric_way:
                raise ValueError('NumpyWavefunctionSimulator() backend'
                        ' does not yet support parametric programs.')
            else:
                if packed_amps is not None:
                    self.ansatz = uccsd_ansatz_circuit(
                            packed_amps,
                            self.molecule.n_orbitals,
                            self.molecule.n_electrons,
                            cq=self.custom_qubits)
                E += NumpyWavefunctionSimulator(n_qubits=self.n_qubits).\
                    do_program(self.ref_state+self.ansatz).\
                    expectation(self.pauli_sum).real
        elif self.method == 'linalg':
            # check if molecule has data sufficient to construct UCCSD ansatz
            # and propagate starting from HF state
            if self.molecule is not None:
                propagator = normal_ordered(
                        uccsd_singlet_generator(
                                packed_amps,
                                2 * self.molecule.n_orbitals,
                                self.molecule.n_electrons,
                                anti_hermitian=True))
                qubit_propagator_matrix = get_sparse_operator(
                        propagator,
                        n_qubits=self.n_qubits)
                uccsd_state = expm_multiply(qubit_propagator_matrix,
                        self.initial_psi)
                expected_uccsd_energy = expectation(
                        self.hamiltonian_matrix, uccsd_state).real
                E += expected_uccsd_energy
            else:
                # apparently no molecule was supplied;
                # attempt to just propagate the ansatz from user-specified
                # initial state, using a circuit unitary
                # if supplied by the user, otherwise the initial state itself,
                # and then estimate over <H>
                if self.initial_psi is None:
                    raise ValueError('Warning: no initial wavefunction set.'
                            ' Please set using '
                            'VQEexperiment().set_initial_state()')
                # attempt to propagate with a circuit unitary
                if self.circuit_unitary is None:
                    psi = self.initial_psi
                else:
                    psi = expm_multiply(self.circuit_unitary, self.initial_psi)
                E += expectation(self.hamiltonian_matrix, psi).real
        else:
            raise ValueError('Impossible method: please choose from method'
                    ' = {WFS, Numpy, linalg} if Tomography is set'
                    ' to False, or choose from method = '
                    '{QC, WFS, Numpy, linalg} if tomography is set to True')
        E = E.real
        if self.verbose:
            self.it_num += 1
            print('black-box function call #' + str(self.it_num))
            print('Energy estimate is now:  ' + str(E))
            print('at angles:               ', packed_amps)
            print('and this took ' + '{0:.3f}'.format(time.time()-t) + \
                    ' seconds to evaluate')
        self.history.append(E)
        return E
示例#8
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)
示例#9
0
def expectation_value(operator, state):
    f = filter_terms(normal_ordered(operator))
    return expectation(get_sparse_operator(f, n_qubits=active_spin_orbitals),
                       state) if len(f.terms) else 0j