def run(
        self, qmolecule: QMolecule
    ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]:
        """ run method"""
        logger.debug('Processing started...')
        # Save these values for later combination with the quantum computation result
        self._hf_energy = qmolecule.hf_energy
        self._nuclear_repulsion_energy = qmolecule.nuclear_repulsion_energy
        self._nuclear_dipole_moment = qmolecule.nuclear_dipole_moment
        self._reverse_dipole_sign = qmolecule.reverse_dipole_sign

        core_list = qmolecule.core_orbitals if self._freeze_core else []
        reduce_list = self._orbital_reduction

        if self._freeze_core:
            logger.info(
                "Freeze_core specified. Core orbitals to be frozen: %s",
                core_list)
        if reduce_list:
            logger.info("Configured orbital reduction list: %s", reduce_list)
            reduce_list = [
                x + qmolecule.num_orbitals if x < 0 else x for x in reduce_list
            ]

        freeze_list = []
        remove_list = []

        # Orbitals are specified by their index from 0 to n-1, where n is the number of orbitals the
        # molecule has. The combined list of the core orbitals, when freeze_core is true, with any
        # user supplied orbitals is what will be used. Negative numbers may be used to indicate the
        # upper virtual orbitals, so -1 is the highest, then -2 etc. and these will
        # be converted to the
        # positive 0-based index for computation.
        # In the combined list any orbitals that are occupied are added to a freeze list and an
        # energy is stored from these orbitals to be added later.
        # Unoccupied orbitals are just discarded.
        # Because freeze and eliminate is done in separate steps,
        # with freeze first, we have to re-base
        # the indexes for elimination according to how many orbitals were removed when freezing.
        #
        orb_list = list(set(core_list + reduce_list))
        num_alpha = qmolecule.num_alpha
        num_beta = qmolecule.num_beta
        new_num_alpha = num_alpha
        new_num_beta = num_beta
        if orb_list:
            orbitals_list = np.array(orb_list)
            orbitals_list = \
                orbitals_list[(cast(np.ndarray, orbitals_list) >= 0) &
                              (orbitals_list < qmolecule.num_orbitals)]

            freeze_list_alpha = [i for i in orbitals_list if i < num_alpha]
            freeze_list_beta = [i for i in orbitals_list if i < num_beta]
            freeze_list = np.append(
                freeze_list_alpha,
                [i + qmolecule.num_orbitals for i in freeze_list_beta])

            remove_list_alpha = [i for i in orbitals_list if i >= num_alpha]
            remove_list_beta = [i for i in orbitals_list if i >= num_beta]
            rla_adjust = -len(freeze_list_alpha)
            rlb_adjust = -len(freeze_list_alpha) - len(
                freeze_list_beta) + qmolecule.num_orbitals
            remove_list = np.append(
                [i + rla_adjust for i in remove_list_alpha],
                [i + rlb_adjust for i in remove_list_beta])

            logger.info("Combined orbital reduction list: %s", orbitals_list)
            logger.info(
                "  converting to spin orbital reduction list: %s",
                np.append(np.array(orbitals_list),
                          np.array(orbitals_list) + qmolecule.num_orbitals))
            logger.info("    => freezing spin orbitals: %s", freeze_list)
            logger.info(
                "    => removing spin orbitals: %s (indexes accounting for freeze %s)",
                np.append(remove_list_alpha,
                          np.array(remove_list_beta) + qmolecule.num_orbitals),
                remove_list)

            new_num_alpha -= len(freeze_list_alpha)
            new_num_beta -= len(freeze_list_beta)

        new_nel = [new_num_alpha, new_num_beta]

        fer_op = FermionicOperator(h1=qmolecule.one_body_integrals,
                                   h2=qmolecule.two_body_integrals)
        fer_op, self._energy_shift, did_shift = \
            Hamiltonian._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list)
        if did_shift:
            logger.info("Frozen orbital energy shift: %s", self._energy_shift)
        if self._transformation == TransformationType.PARTICLE_HOLE.value:
            fer_op, ph_shift = fer_op.particle_hole_transformation(new_nel)
            self._ph_energy_shift = -ph_shift
            logger.info("Particle hole energy shift: %s",
                        self._ph_energy_shift)
        logger.debug('Converting to qubit using %s mapping',
                     self._qubit_mapping)
        qubit_op = Hamiltonian._map_fermionic_operator_to_qubit(
            fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction)
        qubit_op.name = 'Electronic Hamiltonian'

        logger.debug('  num paulis: %s, num qubits: %s', len(qubit_op.paulis),
                     qubit_op.num_qubits)

        aux_ops = []

        def _add_aux_op(aux_op, name):
            aux_qop = Hamiltonian._map_fermionic_operator_to_qubit(
                aux_op, self._qubit_mapping, new_nel,
                self._two_qubit_reduction)
            aux_qop.name = name
            aux_ops.append(aux_qop)
            logger.debug('  num paulis: %s', aux_qop.paulis)

        logger.debug('Creating aux op for Number of Particles')
        _add_aux_op(fer_op.total_particle_number(), 'Number of Particles')
        logger.debug('Creating aux op for S^2')
        _add_aux_op(fer_op.total_angular_momentum(), 'S^2')
        logger.debug('Creating aux op for Magnetization')
        _add_aux_op(fer_op.total_magnetization(), 'Magnetization')

        if qmolecule.has_dipole_integrals():

            def _dipole_op(dipole_integrals, axis):
                logger.debug('Creating aux op for dipole %s', axis)
                fer_op_ = FermionicOperator(h1=dipole_integrals)
                fer_op_, shift, did_shift_ = self._try_reduce_fermionic_operator(
                    fer_op_, freeze_list, remove_list)
                if did_shift_:
                    logger.info("Frozen orbital %s dipole shift: %s", axis,
                                shift)
                ph_shift_ = 0.0
                if self._transformation == TransformationType.PARTICLE_HOLE.value:
                    fer_op_, ph_shift_ = fer_op_.particle_hole_transformation(
                        new_nel)
                    ph_shift_ = -ph_shift_
                    logger.info("Particle hole %s dipole shift: %s", axis,
                                ph_shift_)
                qubit_op_ = self._map_fermionic_operator_to_qubit(
                    fer_op_, self._qubit_mapping, new_nel,
                    self._two_qubit_reduction)
                qubit_op_.name = 'Dipole ' + axis
                logger.debug('  num paulis: %s', len(qubit_op_.paulis))
                return qubit_op_, shift, ph_shift_

            op_dipole_x, self._x_dipole_shift, self._ph_x_dipole_shift = \
                _dipole_op(qmolecule.x_dipole_integrals, 'x')
            op_dipole_y, self._y_dipole_shift, self._ph_y_dipole_shift = \
                _dipole_op(qmolecule.y_dipole_integrals, 'y')
            op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \
                _dipole_op(qmolecule.z_dipole_integrals, 'z')

            aux_ops.append(op_dipole_x)
            aux_ops.append(op_dipole_y)
            aux_ops.append(op_dipole_z)

        logger.info('Molecule num electrons: %s, remaining for processing: %s',
                    [num_alpha, num_beta], new_nel)
        nspinorbs = qmolecule.num_orbitals * 2
        new_nspinorbs = nspinorbs - len(freeze_list) - len(remove_list)
        logger.info(
            'Molecule num spin orbitals: %s, remaining for processing: %s',
            nspinorbs, new_nspinorbs)

        self._add_molecule_info(self.INFO_NUM_PARTICLES,
                                (new_num_alpha, new_num_beta))
        self._add_molecule_info(self.INFO_NUM_ORBITALS, new_nspinorbs)
        self._add_molecule_info(
            self.INFO_TWO_QUBIT_REDUCTION, self._two_qubit_reduction
            if self._qubit_mapping == 'parity' else False)

        z2symmetries = Z2Symmetries([], [], [], None)
        if self._z2symmetry_reduction is not None:
            logger.debug('Processing z2 symmetries')
            qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(
                qubit_op, aux_ops)
        self._add_molecule_info(self.INFO_Z2SYMMETRIES, z2symmetries)

        logger.debug('Processing complete ready to run algorithm')
        return qubit_op, aux_ops
Exemplo n.º 2
0
    def _do_transform(
        self,
        qmolecule: QMolecule,
        aux_operators: Optional[List[FermionicOperator]] = None
    ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]:
        """
        Args:
            qmolecule: qmolecule
            aux_operators: Additional ``FermionicOperator``s to map to a qubit operator.

        Returns:
            (qubit operator, auxiliary operators)

        """
        logger.debug('Processing started...')
        # Save these values for later combination with the quantum computation result
        self._hf_energy = qmolecule.hf_energy
        self._nuclear_repulsion_energy = qmolecule.nuclear_repulsion_energy
        self._nuclear_dipole_moment = qmolecule.nuclear_dipole_moment
        self._reverse_dipole_sign = qmolecule.reverse_dipole_sign

        core_list = qmolecule.core_orbitals if self._freeze_core else []
        reduce_list = self._orbital_reduction

        if self._freeze_core:
            logger.info(
                "Freeze_core specified. Core orbitals to be frozen: %s",
                core_list)
        if reduce_list:
            logger.info("Configured orbital reduction list: %s", reduce_list)
            reduce_list = [
                x + qmolecule.num_orbitals if x < 0 else x for x in reduce_list
            ]

        freeze_list = []
        remove_list = []

        # Orbitals are specified by their index from 0 to n-1, where n is the number of orbitals the
        # molecule has. The combined list of the core orbitals, when freeze_core is true, with any
        # user supplied orbitals is what will be used. Negative numbers may be used to indicate the
        # upper virtual orbitals, so -1 is the highest, then -2 etc. and these will
        # be converted to the
        # positive 0-based index for computation.
        # In the combined list any orbitals that are occupied are added to a freeze list and an
        # energy is stored from these orbitals to be added later.
        # Unoccupied orbitals are just discarded.
        # Because freeze and eliminate is done in separate steps,
        # with freeze first, we have to re-base
        # the indexes for elimination according to how many orbitals were removed when freezing.
        #
        orb_list = list(set(core_list + reduce_list))
        num_alpha = qmolecule.num_alpha
        num_beta = qmolecule.num_beta
        new_num_alpha = num_alpha
        new_num_beta = num_beta
        if orb_list:
            orbitals_list = np.array(orb_list)
            orbitals_list = orbitals_list[
                (cast(np.ndarray, orbitals_list) >= 0)
                & (orbitals_list < qmolecule.num_orbitals)]

            freeze_list_alpha = [i for i in orbitals_list if i < num_alpha]
            freeze_list_beta = [i for i in orbitals_list if i < num_beta]
            freeze_list = np.append(
                freeze_list_alpha,
                [i + qmolecule.num_orbitals for i in freeze_list_beta])

            remove_list_alpha = [i for i in orbitals_list if i >= num_alpha]
            remove_list_beta = [i for i in orbitals_list if i >= num_beta]
            rla_adjust = -len(freeze_list_alpha)
            rlb_adjust = -len(freeze_list_alpha) - len(
                freeze_list_beta) + qmolecule.num_orbitals
            remove_list = np.append(
                [i + rla_adjust for i in remove_list_alpha],
                [i + rlb_adjust for i in remove_list_beta])

            logger.info("Combined orbital reduction list: %s", orbitals_list)
            logger.info(
                "  converting to spin orbital reduction list: %s",
                np.append(np.array(orbitals_list),
                          np.array(orbitals_list) + qmolecule.num_orbitals))
            logger.info("    => freezing spin orbitals: %s", freeze_list)
            logger.info(
                "    => removing spin orbitals: %s (indexes accounting for freeze %s)",
                np.append(remove_list_alpha,
                          np.array(remove_list_beta) + qmolecule.num_orbitals),
                remove_list)

            new_num_alpha -= len(freeze_list_alpha)
            new_num_beta -= len(freeze_list_beta)

        new_nel = [new_num_alpha, new_num_beta]

        # construct the fermionic operator
        fer_op = FermionicOperator(h1=qmolecule.one_body_integrals,
                                   h2=qmolecule.two_body_integrals)

        # try to reduce it according to the freeze and remove list
        fer_op, self._energy_shift, did_shift = \
            FermionicTransformation._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list)
        # apply same transformation for the aux operators
        if aux_operators is not None:
            aux_operators = [
                FermionicTransformation._try_reduce_fermionic_operator(
                    op, freeze_list, remove_list)[0] for op in aux_operators
            ]

        if did_shift:
            logger.info("Frozen orbital energy shift: %s", self._energy_shift)
        # apply particle hole transformation, if specified
        if self._transformation == FermionicTransformationType.PARTICLE_HOLE.value:
            fer_op, ph_shift = fer_op.particle_hole_transformation(new_nel)
            self._ph_energy_shift = -ph_shift
            logger.info("Particle hole energy shift: %s",
                        self._ph_energy_shift)

            # apply the same transformation for the aux operators
            if aux_operators is not None:
                aux_operators = [
                    op.particle_hole_transformation(new_nel)[0]
                    for op in aux_operators
                ]

        logger.debug('Converting to qubit using %s mapping',
                     self._qubit_mapping)
        qubit_op = FermionicTransformation._map_fermionic_operator_to_qubit(
            fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction)
        qubit_op.name = 'Fermionic Operator'

        logger.debug('  num paulis: %s, num qubits: %s', len(qubit_op.paulis),
                     qubit_op.num_qubits)

        aux_ops = []  # list of the aux operators

        def _add_aux_op(aux_op: FermionicOperator, name: str) -> None:
            """
            Add auxiliary operators

            Args:
                aux_op: auxiliary operators
                name: name

            """
            aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit(
                aux_op, self._qubit_mapping, new_nel,
                self._two_qubit_reduction)
            aux_qop.name = name

            aux_ops.append(aux_qop)
            logger.debug('  num paulis: %s', aux_qop.paulis)

        # the first three operators are hardcoded to number of particles, angular momentum
        # and magnetization in this order
        logger.debug('Creating aux op for Number of Particles')
        _add_aux_op(fer_op.total_particle_number(), 'Number of Particles')
        logger.debug('Creating aux op for S^2')
        _add_aux_op(fer_op.total_angular_momentum(), 'S^2')
        logger.debug('Creating aux op for Magnetization')
        _add_aux_op(fer_op.total_magnetization(), 'Magnetization')

        # the next three are dipole moments, if supported by the qmolecule
        if qmolecule.has_dipole_integrals():
            self._has_dipole_moments = True

            def _dipole_op(dipole_integrals: np.ndarray, axis: str) \
                    -> Tuple[WeightedPauliOperator, float, float]:
                """
                Dipole operators

                Args:
                    dipole_integrals: dipole integrals
                    axis: axis for dipole moment calculation

                Returns:
                    (qubit_op_, shift, ph_shift_)
                """
                logger.debug('Creating aux op for dipole %s', axis)
                fer_op_ = FermionicOperator(h1=dipole_integrals)
                fer_op_, shift, did_shift_ = self._try_reduce_fermionic_operator(
                    fer_op_, freeze_list, remove_list)
                if did_shift_:
                    logger.info("Frozen orbital %s dipole shift: %s", axis,
                                shift)
                ph_shift_ = 0.0
                if self._transformation == FermionicTransformationType.PARTICLE_HOLE.value:
                    fer_op_, ph_shift_ = fer_op_.particle_hole_transformation(
                        new_nel)
                    ph_shift_ = -ph_shift_
                    logger.info("Particle hole %s dipole shift: %s", axis,
                                ph_shift_)
                qubit_op_ = self._map_fermionic_operator_to_qubit(
                    fer_op_, self._qubit_mapping, new_nel,
                    self._two_qubit_reduction)
                qubit_op_.name = 'Dipole ' + axis
                logger.debug('  num paulis: %s', len(qubit_op_.paulis))
                return qubit_op_, shift, ph_shift_

            op_dipole_x, self._x_dipole_shift, self._ph_x_dipole_shift = \
                _dipole_op(qmolecule.x_dipole_integrals, 'x')
            op_dipole_y, self._y_dipole_shift, self._ph_y_dipole_shift = \
                _dipole_op(qmolecule.y_dipole_integrals, 'y')
            op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \
                _dipole_op(qmolecule.z_dipole_integrals, 'z')

            aux_ops += [op_dipole_x, op_dipole_y, op_dipole_z]

        # add user specified auxiliary operators
        if aux_operators is not None:
            for aux_op in aux_operators:
                if hasattr(aux_op, 'name'):
                    name = aux_op.name
                else:
                    name = ''
                _add_aux_op(aux_op, name)

        logger.info('Molecule num electrons: %s, remaining for processing: %s',
                    [num_alpha, num_beta], new_nel)
        nspinorbs = qmolecule.num_orbitals * 2
        new_nspinorbs = nspinorbs - len(freeze_list) - len(remove_list)
        logger.info(
            'Molecule num spin orbitals: %s, remaining for processing: %s',
            nspinorbs, new_nspinorbs)

        self._molecule_info['num_particles'] = (new_num_alpha, new_num_beta)
        self._molecule_info['num_orbitals'] = new_nspinorbs
        reduction = self._two_qubit_reduction if self._qubit_mapping == 'parity' else False
        self._molecule_info['two_qubit_reduction'] = reduction
        self._untapered_qubit_op = qubit_op

        z2symmetries = Z2Symmetries([], [], [], None)
        if self._z2symmetry_reduction is not None:
            logger.debug('Processing z2 symmetries')
            qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(
                qubit_op, aux_ops)
        self._molecule_info['z2_symmetries'] = z2symmetries

        logger.debug('Processing complete ready to run algorithm')
        return qubit_op, aux_ops