def test_measurement_error_mitigation_auto_refresh(self):
        aqua_globals.random_seed = 0

        # build noise model
        noise_model = noise.NoiseModel()
        read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]])
        noise_model.add_all_qubit_readout_error(read_err)

        backend = Aer.get_backend('qasm_simulator')
        quantum_instance = QuantumInstance(backend=backend, seed=1679, seed_transpiler=167,
                                           noise_model=noise_model,
                                           measurement_error_mitigation_cls=CompleteMeasFitter,
                                           cals_matrix_refresh_period=0)
        input = 'a & b & c'
        oracle = LogicalExpressionOracle(input, optimization='off')
        grover = Grover(oracle)
        _ = grover.run(quantum_instance)
        cals_matrix_1 = quantum_instance.cals_matrix.copy()

        time.sleep(15)
        aqua_globals.random_seed = 2
        quantum_instance.set_config(seed=111)
        _ = grover.run(quantum_instance)
        cals_matrix_2 = quantum_instance.cals_matrix.copy()

        diff = cals_matrix_1 - cals_matrix_2
        total_diff = np.sum(np.abs(diff))

        self.assertGreater(total_diff, 0.0)
Exemple #2
0
class QuantumAlgorithm(ABC):
    """
    Base class for Quantum Algorithms.

    This method should initialize the module and
    use an exception if a component of the module is available.
    """
    @abstractmethod
    def __init__(self) -> None:
        self._quantum_instance = None

    @property
    def random(self):
        """Return a numpy random."""
        return aqua_globals.random

    def run(self,
            quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None,
            **kwargs) -> Dict:
        """Execute the algorithm with selected backend.

        Args:
            quantum_instance: the experimental setting.
            kwargs (dict): kwargs
        Returns:
            dict: results of an algorithm.
        Raises:
            AquaError: If a quantum instance or backend has not been provided
        """
        if quantum_instance is None and self.quantum_instance is None:
            raise AquaError("Quantum device or backend "
                            "is needed since you are running quantum algorithm.")
        if isinstance(quantum_instance, BaseBackend):
            self.set_backend(quantum_instance, **kwargs)
        else:
            if quantum_instance is not None:
                self.quantum_instance = quantum_instance

        return self._run()

    @abstractmethod
    def _run(self) -> Dict:
        raise NotImplementedError()

    @property
    def quantum_instance(self) -> Union[None, QuantumInstance]:
        """ returns quantum instance """
        return self._quantum_instance

    @quantum_instance.setter
    def quantum_instance(self, quantum_instance: Union[QuantumInstance, BaseBackend]) -> None:
        """Set quantum  instance."""
        if isinstance(quantum_instance, BaseBackend):
            quantum_instance = QuantumInstance(quantum_instance)
        self._quantum_instance = quantum_instance

    def set_backend(self, backend: BaseBackend, **kwargs) -> None:
        """Set backend with configuration."""
        self._quantum_instance = QuantumInstance(backend)
        self._quantum_instance.set_config(**kwargs)
Exemple #3
0
    def test_measurement_error_mitigation_auto_refresh(self):
        """ measurement error mitigation auto refresh test """
        try:
            # pylint: disable=import-outside-toplevel
            from qiskit import Aer
            from qiskit.providers.aer import noise
        except ImportError as ex:  # pylint: disable=broad-except
            self.skipTest(
                "Aer doesn't appear to be installed. Error: '{}'".format(
                    str(ex)))
            return

        aqua_globals.random_seed = 0

        # build noise model
        noise_model = noise.NoiseModel()
        read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1],
                                                            [0.25, 0.75]])
        noise_model.add_all_qubit_readout_error(read_err)

        backend = Aer.get_backend('qasm_simulator')
        quantum_instance = QuantumInstance(
            backend=backend,
            seed_simulator=1679,
            seed_transpiler=167,
            noise_model=noise_model,
            measurement_error_mitigation_cls=CompleteMeasFitter,
            cals_matrix_refresh_period=0)
        oracle = LogicalExpressionOracle('a & b & c')
        grover = Grover(oracle)
        _ = grover.run(quantum_instance)
        self.assertGreater(quantum_instance.time_taken, 0.)
        quantum_instance.reset_execution_results()
        cals_matrix_1, timestamp_1 = quantum_instance.cals_matrix(
            qubit_index=[0, 1, 2])

        time.sleep(15)
        aqua_globals.random_seed = 2
        quantum_instance.set_config(seed_simulator=111)
        _ = grover.run(quantum_instance)
        cals_matrix_2, timestamp_2 = quantum_instance.cals_matrix(
            qubit_index=[0, 1, 2])

        diff = cals_matrix_1 - cals_matrix_2
        total_diff = np.sum(np.abs(diff))

        self.assertGreater(total_diff, 0.0)
        self.assertGreater(timestamp_2, timestamp_1)
Exemple #4
0
    def run(self, quantum_instance=None, **kwargs):
        """Execute the algorithm with selected backend.

        Args:
            quantum_instance (QuantumInstance or BaseBackend): the experiemental setting.

        Returns:
            dict: results of an algorithm.
        """
        if not self.configuration.get('classical', False):
            if quantum_instance is None:
                AquaError(
                    "Quantum device or backend is needed since you are running quantum algorithm."
                )
            if isinstance(quantum_instance, BaseBackend):
                quantum_instance = QuantumInstance(quantum_instance)
                quantum_instance.set_config(**kwargs)
            self._quantum_instance = quantum_instance
        return self._run()
    def test_w_noise(self):
        """ with noise test """
        # build noise model
        # Asymmetric readout error on qubit-0 only
        try:
            # pylint: disable=import-outside-toplevel
            from qiskit.providers.aer.noise import NoiseModel
            from qiskit import Aer
            self.backend = Aer.get_backend('qasm_simulator')
        except ImportError as ex:  # pylint: disable=broad-except
            self.skipTest(
                "Aer doesn't appear to be installed. Error: '{}'".format(
                    str(ex)))
            return

        probs_given0 = [0.9, 0.1]
        probs_given1 = [0.3, 0.7]
        noise_model = NoiseModel()
        noise_model.add_readout_error([probs_given0, probs_given1], [0])

        quantum_instance = QuantumInstance(self.backend,
                                           seed_transpiler=self.random_seed,
                                           seed_simulator=self.random_seed,
                                           shots=1024,
                                           noise_model=noise_model)
        res_w_noise = quantum_instance.execute(self.qc).get_counts(self.qc)

        quantum_instance.skip_qobj_validation = True
        res_w_noise_skip_validation = quantum_instance.execute(
            self.qc).get_counts(self.qc)
        self.assertTrue(_compare_dict(res_w_noise,
                                      res_w_noise_skip_validation))

        # BasicAer should fail:
        with self.assertRaises(AquaError):
            _ = QuantumInstance(BasicAer.get_backend('qasm_simulator'),
                                noise_model=noise_model)

        with self.assertRaises(AquaError):
            quantum_instance = QuantumInstance(
                BasicAer.get_backend('qasm_simulator'))
            quantum_instance.set_config(noise_model=noise_model)
    def run(self, quantum_instance=None, **kwargs):
        """Execute the algorithm with selected backend.

        Args:
            quantum_instance (QuantumInstance or BaseBackend): the experimental setting.
            kwargs (dict): kwargs
        Returns:
            dict: results of an algorithm.
        """
        # pylint: disable=import-outside-toplevel
        from qiskit.providers import BaseBackend

        if not self.configuration.get('classical', False):
            if quantum_instance is None:
                AquaError("Quantum device or backend "
                          "is needed since you are running quantum algorithm.")
            if isinstance(quantum_instance, BaseBackend):
                quantum_instance = QuantumInstance(quantum_instance)
                quantum_instance.set_config(**kwargs)
            self._quantum_instance = quantum_instance
        return self._run()
class TestTPBGroupedWeightedPauliOperator(QiskitAquaTestCase):
    """TPBGroupedWeightedPauliOperator tests."""
    def setUp(self):
        super().setUp()
        seed = 0
        aqua_globals.random_seed = seed

        self.num_qubits = 3
        paulis = [
            Pauli.from_label(pauli_label)
            for pauli_label in itertools.product('IXYZ',
                                                 repeat=self.num_qubits)
        ]
        weights = aqua_globals.random.random(len(paulis))
        self.qubit_op = WeightedPauliOperator.from_list(paulis, weights)
        warnings.filterwarnings('ignore', category=DeprecationWarning)
        self.var_form = RYRZ(self.qubit_op.num_qubits, 1)
        warnings.filterwarnings('always', category=DeprecationWarning)

        qasm_simulator = BasicAer.get_backend('qasm_simulator')
        self.quantum_instance_qasm = QuantumInstance(qasm_simulator,
                                                     shots=65536,
                                                     seed_simulator=seed,
                                                     seed_transpiler=seed)

        statevector_simulator = BasicAer.get_backend('statevector_simulator')
        self.quantum_instance_statevector = \
            QuantumInstance(statevector_simulator, shots=1,
                            seed_simulator=seed, seed_transpiler=seed)

    def test_sorted_grouping(self):
        """Test with color grouping approach."""
        num_qubits = 2
        paulis = [
            Pauli.from_label(pauli_label)
            for pauli_label in itertools.product('IXYZ', repeat=num_qubits)
        ]
        weights = aqua_globals.random.random(len(paulis))
        op = WeightedPauliOperator.from_list(paulis, weights)
        grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator(
            op, TPBGroupedWeightedPauliOperator.sorted_grouping)

        # check all paulis are still existed.
        for g_p in grouped_op.paulis:
            passed = False
            for pauli in op.paulis:
                if pauli[1] == g_p[1]:
                    passed = pauli[0] == g_p[0]
                    break
            self.assertTrue(
                passed, "non-existed paulis in grouped_paulis: {}".format(
                    g_p[1].to_label()))

        # check the number of basis of grouped
        # one should be less than and equal to the original one.
        self.assertGreaterEqual(len(op.basis), len(grouped_op.basis))

    def test_unsorted_grouping(self):
        """Test with normal grouping approach."""

        num_qubits = 4
        paulis = [
            Pauli.from_label(pauli_label)
            for pauli_label in itertools.product('IXYZ', repeat=num_qubits)
        ]
        weights = aqua_globals.random.random(len(paulis))
        op = WeightedPauliOperator.from_list(paulis, weights)
        grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator(
            op, TPBGroupedWeightedPauliOperator.unsorted_grouping)

        for g_p in grouped_op.paulis:
            passed = False
            for pauli in op.paulis:
                if pauli[1] == g_p[1]:
                    passed = pauli[0] == g_p[0]
                    break
            self.assertTrue(
                passed, "non-existed paulis in grouped_paulis: {}".format(
                    g_p[1].to_label()))

        self.assertGreaterEqual(len(op.basis), len(grouped_op.basis))

    def test_chop(self):
        """ chop test """
        paulis = [
            Pauli.from_label(x)
            for x in ['IIXX', 'ZZXX', 'ZZZZ', 'XXZZ', 'XXXX', 'IXXX']
        ]
        coeffs = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7]
        op = WeightedPauliOperator.from_list(paulis, coeffs)
        grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator(
            op, TPBGroupedWeightedPauliOperator.sorted_grouping)

        original_num_basis = len(grouped_op.basis)
        chopped_grouped_op = grouped_op.chop(0.35, copy=True)
        self.assertLessEqual(len(chopped_grouped_op.basis), 3)
        self.assertLessEqual(len(chopped_grouped_op.basis), original_num_basis)
        # ZZXX group is remove
        for b, _ in chopped_grouped_op.basis:
            self.assertFalse(b.to_label() == 'ZZXX')

        chopped_grouped_op = grouped_op.chop(0.55, copy=True)
        self.assertLessEqual(len(chopped_grouped_op.basis), 1)
        self.assertLessEqual(len(chopped_grouped_op.basis), original_num_basis)

        for b, _ in chopped_grouped_op.basis:
            self.assertFalse(b.to_label() == 'ZZXX')
            self.assertFalse(b.to_label() == 'ZZZZ')
            self.assertFalse(b.to_label() == 'XXZZ')

    def test_evaluate_qasm_mode(self):
        """ evaluate qasm mode test """
        wave_function = self.var_form.construct_circuit(
            np.array(
                aqua_globals.random.standard_normal(
                    self.var_form.num_parameters)))
        wave_fn_statevector = \
            self.quantum_instance_statevector.execute(wave_function).get_statevector(wave_function)
        reference = self.qubit_op.copy().evaluate_with_statevector(
            wave_fn_statevector)

        shots = 65536 // len(self.qubit_op.paulis)
        self.quantum_instance_qasm.set_config(shots=shots)
        circuits = self.qubit_op.construct_evaluation_circuit(
            wave_function=wave_function, statevector_mode=False)
        result = self.quantum_instance_qasm.execute(circuits)
        pauli_value = self.qubit_op.evaluate_with_result(
            result=result, statevector_mode=False)
        grouped_op = op_converter.to_tpb_grouped_weighted_pauli_operator(
            self.qubit_op, TPBGroupedWeightedPauliOperator.sorted_grouping)
        shots = 65536 // grouped_op.num_groups
        self.quantum_instance_qasm.set_config(shots=shots)
        circuits = grouped_op.construct_evaluation_circuit(
            wave_function=wave_function, statevector_mode=False)
        grouped_pauli_value = grouped_op.evaluate_with_result(
            result=self.quantum_instance_qasm.execute(circuits),
            statevector_mode=False)

        self.assertGreaterEqual(
            reference[0].real,
            grouped_pauli_value[0].real - 3 * grouped_pauli_value[1].real)
        self.assertLessEqual(
            reference[0].real,
            grouped_pauli_value[0].real + 3 * grouped_pauli_value[1].real)
        # this check assure the std of grouped pauli is
        # less than pauli mode under a fixed amount of total shots
        self.assertLessEqual(grouped_pauli_value[1].real, pauli_value[1].real)

    def test_equal(self):
        """ equal test """
        gop_1 = op_converter.to_tpb_grouped_weighted_pauli_operator(
            self.qubit_op, TPBGroupedWeightedPauliOperator.sorted_grouping)
        gop_2 = op_converter.to_tpb_grouped_weighted_pauli_operator(
            self.qubit_op, TPBGroupedWeightedPauliOperator.unsorted_grouping)

        self.assertEqual(gop_1, gop_2)
#optimizer = BFGS_Grad(maxfun = maxfun, maxiter = maxiter, factr = factr, pgtol = pgtol)
superoptimizer = SuperL_BFGS_B(maxfun=maxfun,
                               maxiter=maxiter,
                               factr=factr,
                               _num_restarts=_num_restarts)
#optimizer = P_BFGS(maxfun = maxfun, factr = factr, max_processes = 3)
optimizer = mini_optimizer

backend = Aer.get_backend('statevector_simulator')
backend_options = {
    'max_parallel_threads': len(psutil.Process().cpu_affinity()),
    'max_parallel_experiments': 0
}
shots = 1
qi = QuantumInstance(backend, shots=shots)
qi.set_config(**backend_options)

it = HartreeFock(num_qubits=4,
                 num_orbitals=6,
                 num_particles=2,
                 two_qubit_reduction=True,
                 qubit_mapping='parity')
#it = None
#optimizer = Rotosolve(ROTOSOLVE_stopping_energy,ROTOSOLVE_max_iterations, param_per_step = 2)

adapt_data_dict = {
    'hamiltonian': [],
    'eval time': [],
    'num op choice evals': [],
    'num optimizer evals': [],
    'ansz length': [],
class CircuitSampler(ConverterBase):
    """
    The CircuitSampler traverses an Operator and converts any CircuitStateFns into
    approximations of the state function by a DictStateFn or VectorStateFn using a quantum
    backend. Note that in order to approximate the value of the CircuitStateFn, it must 1) send
    state function through a depolarizing channel, which will destroy all phase information and
    2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw
    probability of sampling (which would be the equivalent of sampling the **square** of the
    state function, per the Born rule.

    The CircuitSampler aggressively caches transpiled circuits to handle re-parameterization of
    the same circuit efficiently. If you are converting multiple different Operators,
    you are better off using a different CircuitSampler for each Operator to avoid cache thrashing.
    """
    def __init__(self,
                 backend: Union[BaseBackend, QuantumInstance] = None,
                 statevector: Optional[bool] = None,
                 param_qobj: bool = False,
                 attach_results: bool = False) -> None:
        """
        Args:
            backend: The quantum backend or QuantumInstance to use to sample the circuits.
            statevector: If backend is a statevector backend, whether to replace the
                CircuitStateFns with DictStateFns (from the counts) or VectorStateFns (from the
                statevector). ``None`` will set this argument automatically based on the backend.
            param_qobj: (TODO, not yet available) Whether to use Aer's parameterized Qobj
                capability to avoid re-assembling the circuits.
            attach_results: Whether to attach the data from the backend ``Results`` object for
                a given ``CircuitStateFn``` to an ``execution_results`` field added the converted
                ``DictStateFn`` or ``VectorStateFn``.

        Raises:
            ValueError: Set statevector or param_qobj True when not supported by backend.
        """
        self._quantum_instance = backend if isinstance(backend, QuantumInstance) else\
            QuantumInstance(backend=backend)
        self._statevector = statevector if statevector is not None \
            else self.quantum_instance.is_statevector
        self._param_qobj = param_qobj
        self._attach_results = attach_results

        self._check_quantum_instance_and_modes_consistent()

        # Object state variables
        self._last_op = None
        self._reduced_op_cache = None
        self._circuit_ops_cache = {}
        self._transpiled_circ_cache = None
        self._transpile_before_bind = True
        self._binding_mappings = None

    def _check_quantum_instance_and_modes_consistent(self) -> None:
        """ Checks whether the statevector and param_qobj settings are compatible with the
        backend

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        if self._statevector and not is_statevector_backend(
                self.quantum_instance.backend):
            raise ValueError(
                'Statevector mode for circuit sampling requires statevector '
                'backend, not {}.'.format(self.quantum_instance.backend))

        if self._param_qobj and not is_aer_provider(
                self.quantum_instance.backend):
            raise ValueError('Parameterized Qobj mode requires Aer '
                             'backend, not {}.'.format(
                                 self.quantum_instance.backend))

    @property
    def backend(self) -> BaseBackend:
        """ Returns the backend.

        Returns:
             The backend used by the CircuitSampler
        """
        return self.quantum_instance.backend

    @backend.setter
    def backend(self, backend: BaseBackend):
        """ Sets backend without additional configuration. """
        self.set_backend(backend)

    def set_backend(self, backend: BaseBackend, **kwargs) -> None:
        """ Sets backend with configuration.

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        self.quantum_instance = QuantumInstance(backend)
        self.quantum_instance.set_config(**kwargs)

    @property
    def quantum_instance(self) -> QuantumInstance:
        """ Returns the quantum instance.

        Returns:
             The QuantumInstance used by the CircuitSampler
        """
        return self._quantum_instance

    @quantum_instance.setter
    def quantum_instance(
            self, quantum_instance: Union[QuantumInstance,
                                          BaseBackend]) -> None:
        """ Sets the QuantumInstance.

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        if isinstance(quantum_instance, BaseBackend):
            quantum_instance = QuantumInstance(quantum_instance)
        self._quantum_instance = quantum_instance
        self._check_quantum_instance_and_modes_consistent()

    # pylint: disable=arguments-differ
    def convert(
        self,
        operator: OperatorBase,
        params: Optional[Dict[Union[ParameterExpression, ParameterVector],
                              Union[float, List[float],
                                    List[List[float]]]]] = None
    ) -> OperatorBase:
        r"""
        Converts the Operator to one in which the CircuitStateFns are replaced by
        DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator,
        caches them, calls ``sample_circuits`` below to get their converted replacements,
        and replaces the CircuitStateFns in operator with the replacement StateFns.

        Args:
            operator: The Operator to convert
            params: A dictionary mapping parameters to either single binding values or lists of
                binding values. The dictionary can also contain pairs of ParameterVectors with
                lists of parameters or lists of lists of parameters to bind to them.

        Returns:
            The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns.
        """
        if self._last_op is None or id(operator) != id(self._last_op):
            # Clear caches
            self._last_op = operator
            self._reduced_op_cache = None
            self._circuit_ops_cache = None
            self._transpiled_circ_cache = None
            self._transpile_before_bind = True

        if not self._reduced_op_cache:
            operator_dicts_replaced = operator.to_circuit_op()
            self._reduced_op_cache = operator_dicts_replaced.reduce()

        if not self._circuit_ops_cache:
            self._circuit_ops_cache = {}
            self._extract_circuitstatefns(self._reduced_op_cache)

        if params:
            num_parameterizations = len(list(params.values())[0])
            param_bindings = [{
                param: value_list[i]
                for (param, value_list) in params.items()
            } for i in range(num_parameterizations)]
        else:
            param_bindings = None
            num_parameterizations = 1

        # Don't pass circuits if we have in the cache, the sampling function knows to use the cache
        circs = list(self._circuit_ops_cache.values()
                     ) if not self._transpiled_circ_cache else None
        sampled_statefn_dicts = self.sample_circuits(
            circuit_sfns=circs, param_bindings=param_bindings)

        def replace_circuits_with_dicts(operator, param_index=0):
            if isinstance(operator, CircuitStateFn):
                return sampled_statefn_dicts[id(operator)][param_index]
            elif isinstance(operator, ListOp):
                return operator.traverse(
                    partial(replace_circuits_with_dicts,
                            param_index=param_index))
            else:
                return operator

        if params:
            return ListOp([
                replace_circuits_with_dicts(self._reduced_op_cache,
                                            param_index=i)
                for i in range(num_parameterizations)
            ])
        else:
            return replace_circuits_with_dicts(self._reduced_op_cache,
                                               param_index=0)

    def _extract_circuitstatefns(self, operator: OperatorBase) -> None:
        r"""
        Recursively extract the ``CircuitStateFns`` contained in operator into the
        ``_circuit_ops_cache`` field.
        """
        if isinstance(operator, CircuitStateFn):
            self._circuit_ops_cache[id(operator)] = operator
        elif isinstance(operator, ListOp):
            for op in operator.oplist:
                self._extract_circuitstatefns(op)

    def sample_circuits(
        self,
        circuit_sfns: Optional[List[CircuitStateFn]] = None,
        param_bindings: Optional[List[Dict[ParameterExpression,
                                           List[float]]]] = None
    ) -> Dict[int, Union[StateFn, List[StateFn]]]:
        r"""
        Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their
        replacement DictStateFn or VectorStateFn. If param_bindings is provided,
        the CircuitStateFns are broken into their parameterizations, and a list of StateFns is
        returned in the dict for each circuit ``id()``. Note that param_bindings is provided here
        in a different format than in ``convert``, and lists of parameters within the dict is not
        supported, and only binding dicts which are valid to be passed into Terra can be included
        in this list.

        Args:
            circuit_sfns: The list of CircuitStateFns to sample.
            param_bindings: The parameterizations to bind to each CircuitStateFn.

        Returns:
            The dictionary mapping ids of the CircuitStateFns to their replacement StateFns.
        """
        if circuit_sfns or not self._transpiled_circ_cache:
            if self._statevector:
                circuits = [
                    op_c.to_circuit(meas=False) for op_c in circuit_sfns
                ]
            else:
                circuits = [
                    op_c.to_circuit(meas=True) for op_c in circuit_sfns
                ]

            try:
                self._transpiled_circ_cache = self.quantum_instance.transpile(
                    circuits)
            except QiskitError:
                logger.debug(
                    r'CircuitSampler failed to transpile circuits with unbound '
                    r'parameters. Attempting to transpile only when circuits are bound '
                    r'now, but this can hurt performance due to repeated transpilation.'
                )
                self._transpile_before_bind = False
                self._transpiled_circ_cache = circuits
        else:
            circuit_sfns = list(self._circuit_ops_cache.values())

        if param_bindings is not None:
            if self._param_qobj:
                ready_circs = self._transpiled_circ_cache
                self._prepare_parameterized_run_config(param_bindings)
            else:
                ready_circs = [
                    circ.assign_parameters(binding)
                    for circ in self._transpiled_circ_cache
                    for binding in param_bindings
                ]
        else:
            ready_circs = self._transpiled_circ_cache

        results = self.quantum_instance.execute(
            ready_circs, had_transpiled=self._transpile_before_bind)

        # Wipe parameterizations, if any
        # self.quantum_instance._run_config.parameterizations = None

        sampled_statefn_dicts = {}
        for i, op_c in enumerate(circuit_sfns):
            # Taking square root because we're replacing a statevector
            # representation of probabilities.
            reps = len(param_bindings) if param_bindings is not None else 1
            c_statefns = []
            for j in range(reps):
                circ_index = (i * reps) + j
                circ_results = results.data(circ_index)

                if 'expval_measurement' in circ_results.get(
                        'snapshots', {}).get('expectation_value', {}):
                    snapshot_data = results.data(circ_index)['snapshots']
                    avg = snapshot_data['expectation_value'][
                        'expval_measurement'][0]['value']
                    if isinstance(avg, (list, tuple)):
                        # Aer versions before 0.4 use a list snapshot format
                        # which must be converted to a complex value.
                        avg = avg[0] + 1j * avg[1]
                    # Will be replaced with just avg when eval is called later
                    num_qubits = circuit_sfns[0].num_qubits
                    result_sfn = (Zero ^ num_qubits).adjoint() * avg
                elif self._statevector:
                    result_sfn = StateFn(op_c.coeff *
                                         results.get_statevector(circ_index))
                else:
                    shots = self.quantum_instance._run_config.shots
                    result_sfn = StateFn({
                        b: (v * op_c.coeff / shots)**.5
                        for (b, v) in results.get_counts(circ_index).items()
                    })
                if self._attach_results:
                    result_sfn.execution_results = circ_results
                c_statefns.append(result_sfn)
            sampled_statefn_dicts[id(op_c)] = c_statefns
        return sampled_statefn_dicts

    # TODO build Aer re-parameterized Qobj.
    def _prepare_parameterized_run_config(self, param_bindings: dict) -> None:
        raise NotImplementedError
Exemple #10
0
class CircuitSampler(ConverterBase):
    """
    The CircuitSampler traverses an Operator and converts any CircuitStateFns into
    approximations of the state function by a DictStateFn or VectorStateFn using a quantum
    backend. Note that in order to approximate the value of the CircuitStateFn, it must 1) send
    state function through a depolarizing channel, which will destroy all phase information and
    2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw
    probability of sampling (which would be the equivalent of sampling the **square** of the
    state function, per the Born rule.

    The CircuitSampler aggressively caches transpiled circuits to handle re-parameterization of
    the same circuit efficiently. If you are converting multiple different Operators,
    you are better off using a different CircuitSampler for each Operator to avoid cache thrashing.
    """
    def __init__(self,
                 backend: Union[Backend, BaseBackend, QuantumInstance],
                 statevector: Optional[bool] = None,
                 param_qobj: bool = False,
                 attach_results: bool = False) -> None:
        """
        Args:
            backend: The quantum backend or QuantumInstance to use to sample the circuits.
            statevector: If backend is a statevector backend, whether to replace the
                CircuitStateFns with DictStateFns (from the counts) or VectorStateFns (from the
                statevector). ``None`` will set this argument automatically based on the backend.
            attach_results: Whether to attach the data from the backend ``Results`` object for
                a given ``CircuitStateFn``` to an ``execution_results`` field added the converted
                ``DictStateFn`` or ``VectorStateFn``.
            param_qobj: Whether to use Aer's parameterized Qobj capability to avoid re-assembling
                the circuits.

        Raises:
            ValueError: Set statevector or param_qobj True when not supported by backend.
        """
        self._quantum_instance = backend if isinstance(backend, QuantumInstance) else\
            QuantumInstance(backend=backend)
        self._statevector = statevector if statevector is not None \
            else self.quantum_instance.is_statevector
        self._param_qobj = param_qobj
        self._attach_results = attach_results

        self._check_quantum_instance_and_modes_consistent()

        # Object state variables
        self._last_op = None
        self._reduced_op_cache = None
        self._circuit_ops_cache = {}  # type: Dict[int, CircuitStateFn]
        self._transpiled_circ_cache = None  # type: Optional[List[Any]]
        self._transpiled_circ_templates = None  # type: Optional[List[Any]]
        self._transpile_before_bind = True
        self._binding_mappings = None

    def _check_quantum_instance_and_modes_consistent(self) -> None:
        """ Checks whether the statevector and param_qobj settings are compatible with the
        backend

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        if self._statevector and not is_statevector_backend(
                self.quantum_instance.backend):
            raise ValueError(
                'Statevector mode for circuit sampling requires statevector '
                'backend, not {}.'.format(self.quantum_instance.backend))

        if self._param_qobj and not is_aer_provider(
                self.quantum_instance.backend):
            raise ValueError('Parameterized Qobj mode requires Aer '
                             'backend, not {}.'.format(
                                 self.quantum_instance.backend))

    @property
    def backend(self) -> Union[Backend, BaseBackend]:
        """ Returns the backend.

        Returns:
             The backend used by the CircuitSampler
        """
        return self.quantum_instance.backend

    @backend.setter
    def backend(self, backend: Union[Backend, BaseBackend]):
        """ Sets backend without additional configuration. """
        self.set_backend(backend)

    def set_backend(self, backend: Union[Backend, BaseBackend],
                    **kwargs) -> None:
        """ Sets backend with configuration.

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        self.quantum_instance = QuantumInstance(backend)
        self.quantum_instance.set_config(**kwargs)

    @property
    def quantum_instance(self) -> QuantumInstance:
        """ Returns the quantum instance.

        Returns:
             The QuantumInstance used by the CircuitSampler
        """
        return self._quantum_instance

    @quantum_instance.setter
    def quantum_instance(
        self, quantum_instance: Union[QuantumInstance, Backend,
                                      BaseBackend]) -> None:
        """ Sets the QuantumInstance.

        Raises:
            ValueError: statevector or param_qobj are True when not supported by backend.
        """
        if isinstance(quantum_instance, (Backend, BaseBackend)):
            quantum_instance = QuantumInstance(quantum_instance)
        self._quantum_instance = quantum_instance
        self._check_quantum_instance_and_modes_consistent()

    # pylint: disable=arguments-differ
    def convert(
        self,
        operator: OperatorBase,
        params: Optional[Dict[Parameter, Union[float, List[float],
                                               List[List[float]]]]] = None
    ) -> OperatorBase:
        r"""
        Converts the Operator to one in which the CircuitStateFns are replaced by
        DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator,
        caches them, calls ``sample_circuits`` below to get their converted replacements,
        and replaces the CircuitStateFns in operator with the replacement StateFns.

        Args:
            operator: The Operator to convert
            params: A dictionary mapping parameters to either single binding values or lists of
                binding values.

        Returns:
            The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns.
        Raises:
            AquaError: if extracted circuits are empty.
        """
        if self._last_op is None or id(operator) != id(self._last_op):
            # Clear caches
            self._last_op = operator
            self._reduced_op_cache = None
            self._circuit_ops_cache = None
            self._transpiled_circ_cache = None
            self._transpile_before_bind = True

        if not self._reduced_op_cache:
            operator_dicts_replaced = operator.to_circuit_op()
            self._reduced_op_cache = operator_dicts_replaced.reduce()

        if not self._circuit_ops_cache:
            self._circuit_ops_cache = {}
            self._extract_circuitstatefns(self._reduced_op_cache)
            if not self._circuit_ops_cache:
                raise AquaError(
                    'Circuits are empty. '
                    'Check that the operator is an instance of CircuitStateFn or its ListOp.'
                )

        if params is not None and len(params.keys()) > 0:
            p_0 = list(params.values())[0]  # type: ignore
            if isinstance(p_0, (list, np.ndarray)):
                num_parameterizations = len(cast(List, p_0))
                param_bindings = [
                    {
                        param: value_list[i]  # type: ignore
                        for (param, value_list) in params.items()
                    } for i in range(num_parameterizations)
                ]
            else:
                num_parameterizations = 1
                param_bindings = [params]  # type: ignore

        else:
            param_bindings = None
            num_parameterizations = 1

        # Don't pass circuits if we have in the cache, the sampling function knows to use the cache
        circs = list(self._circuit_ops_cache.values()
                     ) if not self._transpiled_circ_cache else None
        p_b = cast(List[Dict[Parameter, float]], param_bindings)
        sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs,
                                                     param_bindings=p_b)

        def replace_circuits_with_dicts(operator, param_index=0):
            if isinstance(operator, CircuitStateFn):
                return sampled_statefn_dicts[id(operator)][param_index]
            elif isinstance(operator, ListOp):
                return operator.traverse(
                    partial(replace_circuits_with_dicts,
                            param_index=param_index))
            else:
                return operator

        if params:
            return ListOp([
                replace_circuits_with_dicts(self._reduced_op_cache,
                                            param_index=i)
                for i in range(num_parameterizations)
            ])
        else:
            return replace_circuits_with_dicts(self._reduced_op_cache,
                                               param_index=0)

    def _extract_circuitstatefns(self, operator: OperatorBase) -> None:
        r"""
        Recursively extract the ``CircuitStateFns`` contained in operator into the
        ``_circuit_ops_cache`` field.
        """
        if isinstance(operator, CircuitStateFn):
            self._circuit_ops_cache[id(operator)] = operator
        elif isinstance(operator, ListOp):
            for op in operator.oplist:
                self._extract_circuitstatefns(op)

    def sample_circuits(
        self,
        circuit_sfns: Optional[List[CircuitStateFn]] = None,
        param_bindings: Optional[List[Dict[Parameter, float]]] = None
    ) -> Dict[int, Union[StateFn, List[StateFn]]]:
        r"""
        Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their
        replacement DictStateFn or VectorStateFn. If param_bindings is provided,
        the CircuitStateFns are broken into their parameterizations, and a list of StateFns is
        returned in the dict for each circuit ``id()``. Note that param_bindings is provided here
        in a different format than in ``convert``, and lists of parameters within the dict is not
        supported, and only binding dicts which are valid to be passed into Terra can be included
        in this list.

        Args:
            circuit_sfns: The list of CircuitStateFns to sample.
            param_bindings: The parameterizations to bind to each CircuitStateFn.

        Returns:
            The dictionary mapping ids of the CircuitStateFns to their replacement StateFns.
        Raises:
            AquaError: if extracted circuits are empty.
        """
        if not circuit_sfns and not self._transpiled_circ_cache:
            raise AquaError('CircuitStateFn is empty and there is no cache.')

        if circuit_sfns:
            self._transpiled_circ_templates = None
            if self._statevector:
                circuits = [
                    op_c.to_circuit(meas=False) for op_c in circuit_sfns
                ]
            else:
                circuits = [
                    op_c.to_circuit(meas=True) for op_c in circuit_sfns
                ]

            try:
                self._transpiled_circ_cache = self.quantum_instance.transpile(
                    circuits)
            except QiskitError:
                logger.debug(
                    r'CircuitSampler failed to transpile circuits with unbound '
                    r'parameters. Attempting to transpile only when circuits are bound '
                    r'now, but this can hurt performance due to repeated transpilation.'
                )
                self._transpile_before_bind = False
                self._transpiled_circ_cache = circuits
        else:
            circuit_sfns = list(self._circuit_ops_cache.values())

        if param_bindings is not None:
            if self._param_qobj:
                start_time = time()
                ready_circs = self._prepare_parameterized_run_config(
                    param_bindings)
                end_time = time()
                logger.debug('Parameter conversion %.5f (ms)',
                             (end_time - start_time) * 1000)
            else:
                start_time = time()
                ready_circs = [
                    circ.assign_parameters(_filter_params(circ, binding))
                    for circ in self._transpiled_circ_cache
                    for binding in param_bindings
                ]
                end_time = time()
                logger.debug('Parameter binding %.5f (ms)',
                             (end_time - start_time) * 1000)
        else:
            ready_circs = self._transpiled_circ_cache

        results = self.quantum_instance.execute(
            ready_circs, had_transpiled=self._transpile_before_bind)

        if param_bindings is not None and self._param_qobj:
            self._clean_parameterized_run_config()

        # Wipe parameterizations, if any
        # self.quantum_instance._run_config.parameterizations = None

        sampled_statefn_dicts = {}
        for i, op_c in enumerate(circuit_sfns):
            # Taking square root because we're replacing a statevector
            # representation of probabilities.
            reps = len(param_bindings) if param_bindings is not None else 1
            c_statefns = []
            for j in range(reps):
                circ_index = (i * reps) + j
                circ_results = results.data(circ_index)

                if 'expval_measurement' in circ_results.get(
                        'snapshots', {}).get('expectation_value', {}):
                    snapshot_data = results.data(circ_index)['snapshots']
                    avg = snapshot_data['expectation_value'][
                        'expval_measurement'][0]['value']
                    if isinstance(avg, (list, tuple)):
                        # Aer versions before 0.4 use a list snapshot format
                        # which must be converted to a complex value.
                        avg = avg[0] + 1j * avg[1]
                    # Will be replaced with just avg when eval is called later
                    num_qubits = circuit_sfns[0].num_qubits
                    result_sfn = DictStateFn(
                        '0' * num_qubits,
                        is_measurement=op_c.is_measurement) * avg
                elif self._statevector:
                    result_sfn = StateFn(op_c.coeff *
                                         results.get_statevector(circ_index),
                                         is_measurement=op_c.is_measurement)
                else:
                    shots = self.quantum_instance._run_config.shots
                    result_sfn = StateFn(
                        {
                            b: (v / shots)**0.5 * op_c.coeff
                            for (b,
                                 v) in results.get_counts(circ_index).items()
                        },
                        is_measurement=op_c.is_measurement)
                if self._attach_results:
                    result_sfn.execution_results = circ_results
                c_statefns.append(result_sfn)
            sampled_statefn_dicts[id(op_c)] = c_statefns
        return sampled_statefn_dicts

    def _build_aer_params(self, circuit: QuantumCircuit,
                          building_param_tables: Dict[Tuple[int, int],
                                                      List[float]],
                          input_params: Dict[Parameter, float]) -> None:
        def resolve_param(inst_param):
            if not isinstance(inst_param, ParameterExpression):
                return None
            param_mappings = {}
            for param in inst_param._parameter_symbols.keys():
                if param not in input_params:
                    raise ValueError('unexpected parameter: {0}'.format(param))
                param_mappings[param] = input_params[param]
            return float(inst_param.bind(param_mappings))

        gate_index = 0
        for inst, _, _ in circuit.data:
            param_index = 0
            for inst_param in inst.params:
                val = resolve_param(inst_param)
                if val is not None:
                    param_key = (gate_index, param_index)
                    if param_key in building_param_tables:
                        building_param_tables[param_key].append(val)
                    else:
                        building_param_tables[param_key] = [val]
                param_index += 1
            gate_index += 1

    def _prepare_parameterized_run_config(
            self, param_bindings: List[Dict[Parameter, float]]) -> List[Any]:

        self.quantum_instance._run_config.parameterizations = []

        if self._transpiled_circ_templates is None \
                or len(self._transpiled_circ_templates) != len(self._transpiled_circ_cache):

            # temporally resolve parameters of self._transpiled_circ_cache
            # They will be overridden in Aer from the next iterations
            self._transpiled_circ_templates = [
                circ.assign_parameters(_filter_params(circ, param_bindings[0]))
                for circ in self._transpiled_circ_cache
            ]

        for circ in self._transpiled_circ_cache:
            building_param_tables = {
            }  # type: Dict[Tuple[int, int], List[float]]
            for param_binding in param_bindings:
                self._build_aer_params(circ, building_param_tables,
                                       param_binding)
            param_tables = []
            for gate_and_param_indices in building_param_tables:
                gate_index = gate_and_param_indices[0]
                param_index = gate_and_param_indices[1]
                param_tables.append([[gate_index, param_index],
                                     building_param_tables[(gate_index,
                                                            param_index)]])
            self.quantum_instance._run_config.parameterizations.append(
                param_tables)

        return self._transpiled_circ_templates

    def _clean_parameterized_run_config(self) -> None:
        self.quantum_instance._run_config.parameterizations = []