Esempio n. 1
0
    def from_backend(cls, backend: BaseBackend):
        """Construct an :class:`InstructionDurations` object from the backend.

        Args:
            backend: backend from which durations (gate lengths) and dt are extracted.

        Returns:
            InstructionDurations: The InstructionDurations constructed from backend.

        Raises:
            TranspilerError: If dt and dtm is different in the backend.
        """
        # All durations in seconds in gate_length
        instruction_durations = []
        for gate, insts in backend.properties()._gates.items():
            for qubits, props in insts.items():
                if 'gate_length' in props:
                    gate_length = props['gate_length'][
                        0]  # Throw away datetime at index 1
                    instruction_durations.append(
                        (gate, qubits, gate_length, 's'))
        for q, props in backend.properties()._qubits.items():
            if 'readout_length' in props:
                readout_length = props['readout_length'][
                    0]  # Throw away datetime at index 1
                instruction_durations.append(
                    ('measure', [q], readout_length, 's'))

        try:
            dt = backend.configuration().dt  # pylint: disable=invalid-name
        except AttributeError:
            dt = None

        return InstructionDurations(instruction_durations, dt=dt)
    def create_from_backend(cls, backend: BaseBackend):
        """Initialize a class with backend information provided by provider.

        Args:
            backend: Backend object.

        Returns:
            OpenPulseBackendInfo: New configured instance.
        """
        configuration = backend.configuration()
        defaults = backend.defaults()

        # load name
        name = backend.name()

        # load cycle time
        dt = configuration.dt

        # load frequencies
        chan_freqs = dict()

        chan_freqs.update({
            pulse.DriveChannel(qind): freq
            for qind, freq in enumerate(defaults.qubit_freq_est)
        })
        chan_freqs.update({
            pulse.MeasureChannel(qind): freq
            for qind, freq in enumerate(defaults.meas_freq_est)
        })
        for qind, u_lo_mappers in enumerate(configuration.u_channel_lo):
            temp_val = .0 + .0j
            for u_lo_mapper in u_lo_mappers:
                temp_val += defaults.qubit_freq_est[
                    u_lo_mapper.q] * u_lo_mapper.scale
            chan_freqs[pulse.ControlChannel(qind)] = temp_val.real

        # load qubit channel mapping
        qubit_channel_map = defaultdict(list)
        for qind in range(configuration.n_qubits):
            qubit_channel_map[qind].append(configuration.drive(qubit=qind))
            qubit_channel_map[qind].append(configuration.measure(qubit=qind))
            for tind in range(configuration.n_qubits):
                try:
                    qubit_channel_map[qind].extend(
                        configuration.control(qubits=(qind, tind)))
                except BackendConfigurationError:
                    pass

        return OpenPulseBackendInfo(name=name,
                                    dt=dt,
                                    channel_frequency_map=chan_freqs,
                                    qubit_channel_map=qubit_channel_map)
Esempio n. 3
0
    def from_backend(cls, backend: BaseBackend, **kwargs):
        """Automatically loads a QuaC noise model given a backend of type BaseBackend. Primarily
        for speeding up definition of IBMQ hardware noise models in QuaC

        :param backend: an object of type BaseBackend
        :param kwargs: an optional dictionary mapping strings to booleans stating which types
            of noise to include (keys are t1, t2, meas, and zz)
        :return: a QuacNoiseModel object
        """
        n_qubits = len(backend.properties().qubits)
        qubits = list(range(n_qubits))

        # Set up defaults
        t1_times = [float('inf') for _ in range(n_qubits)]
        t2_times = [float('inf') for _ in range(n_qubits)]
        meas_matrices = None
        zz = None

        # Adjust defaults as appropriate
        if kwargs.get("t1"):
            t1_times = [
                backend.properties().t1(qubit) * 1e9 for qubit in qubits
            ]
        if kwargs.get("t2"):
            t2_times = [
                backend.properties().t2(qubit) * 1e9 for qubit in qubits
            ]
        if kwargs.get("meas"):
            meas_matrices = []
            # Construct probability matrix for measurement error adjustments
            for qubit in range(n_qubits):
                # Not all backends have measurement errors added
                try:
                    prob_meas0_prep1 = backend.properties().qubit_property(
                        qubit, "prob_meas0_prep1")[0]
                    prob_meas1_prep0 = backend.properties().qubit_property(
                        qubit, "prob_meas1_prep0")[0]
                except BackendPropertyError:
                    warnings.warn(
                        "Measurement error simulation not supported on this backend"
                    )
                    break

                qubit_measurement_error_matrix = np.array(
                    [[1 - prob_meas1_prep0, prob_meas0_prep1],
                     [prob_meas1_prep0, 1 - prob_meas0_prep1]])

                meas_matrices.append(qubit_measurement_error_matrix)
        if kwargs.get("zz"):
            warnings.warn("ZZ coupling not supported in automatic loading")

        return QuacNoiseModel(t1_times, t2_times, meas_matrices, zz)
Esempio n. 4
0
def optimize_noise_model_ng(
        guess_noise_model: QuacNoiseModel, circuits: List[QuantumCircuit],
        backend: BaseBackend, reference_result: Result,
        loss_function: Callable[[np.array], float]) -> QuacNoiseModel:
    """Refines T1 and T2 noise parameters until the divergence between the probability distributions
    of a provided list of circuits simulated with the plugin and run with the hardware is minimized
    using the Nevergrad TwoPointsDE optimizer

    :param guess_noise_model: a QuacNoiseModel object as an initial ideal guess
    :param circuits: a list of QuantumCircuit objects for loss computation
    :param backend: the backend simulator on which circuits should be run
    :param reference_result: the Qiskit Result object from running circuits on real hardware
    :param loss_function: the loss function that should be used for optimization (i.e., kl_objective_function)
    :return: an optimized QuacNoiseModel object
    """
    arr = ng.p.Array(init=guess_noise_model.to_array())
    arr.set_bounds(0, float('inf'))
    param = ng.p.Instrumentation(arr, circuits, backend, reference_result)

    optimizer = ng.optimizers.TwoPointsDE(parametrization=param,
                                          budget=100,
                                          num_workers=5)
    print(optimizer.num_workers)
    with futures.ThreadPoolExecutor(
            max_workers=optimizer.num_workers) as executor:
        result = optimizer.minimize(loss_function,
                                    executor=executor,
                                    batch_mode=True,
                                    verbosity=2)

    print(result[0][0].value)
    return QuacNoiseModel.from_array(result[0][0].value,
                                     backend.configuration().n_qubits)
Esempio n. 5
0
    def __init__(self, backend: BaseBackend, optimisation_level: int = 1):
        """Identifies a Qiskit backend and provides the corresponding default
        compilation pass from pytket as a
        :py:class:`qiskit.transpiler.TransformationPass`.

        :param backend: The Qiskit backend to target. Accepts Aer or IBMQ backends.
        :type backend: BaseBackend
        :param optimisation_level: The level of optimisation to perform during
            compilation. Level 0 just solves the device constraints without
            optimising. Level 1 additionally performs some light optimisations.
            Level 2 adds more intensive optimisations that can increase compilation
            time for large circuits. Defaults to 1.
        :type optimisation_level: int, optional
        """
        if not isinstance(backend, BaseBackend):
            raise ValueError("Requires BaseBackend instance")
        if isinstance(backend._provider, AerProvider):
            tk_backend = self._aer_backend_map[type(backend).__name__]()
        elif isinstance(backend._provider, AccountProvider):
            tk_backend = IBMQBackend(backend.name())
        else:
            raise NotImplementedError(
                "This backend provider is not supported.")
        super().__init__(
            tk_backend.default_compilation_pass(optimisation_level))
Esempio n. 6
0
def quac_time_qasm_transpiler(circuit: QuantumCircuit,
                              backend: BaseBackend) -> str:
    """Converts a circuit of type QuantumCircuit to a string of TIMEQASM specification

    :param circuit: a QuantumCircuit (need not be transpiled)
    :param backend: a specific backend to generate the QASM for (for tranpsilation)
    :return: a string containing necessary QASM with times for each gate
    """
    # Get original QASM
    transpiled_circuit = transpile(circuit, backend)
    original_qasm = transpiled_circuit.qasm()

    # Get body of original QASM
    start = 2 + len(circuit.qregs) + len(circuit.cregs)
    original_qasm_body = original_qasm.splitlines()[start:]

    # Formulate header
    qasm_modified = "TIMEQASM 1.0;\n"
    qasm_modified += "\n".join(original_qasm.splitlines()[1:start])
    qasm_modified += "\n"
    # Schedule circuit
    qobj = assemble(transpiled_circuit, backend)
    qexp = qobj.experiments[0]
    qschedule = list_schedule_experiment(qexp, backend.properties())

    # Formulate body
    for instruction, time in qschedule:
        # Note: ID was custom injected by the scheduler in this plugin
        qasm_modified += f"{original_qasm_body[instruction.id][:-1]} @{time};\n"

    return qasm_modified
Esempio n. 7
0
    def __validate_number_of_clbits(self, experiment: QasmQobjExperiment) -> None:
        """ Checks whether the number of classical bits has a value cQASM can support.

            1. When number of classical bits is less than 1 an error is raised.
            2. When binary controlled gates are used and the number of classical registers > number of classical
            registers an error is raised.
                When using binary controlled gates in Qiskit, we can have something like:
                q = QuantumRegister(2)
                c = ClassicalRegister(4)
                circuit = QuantumCircuit(q, c)
                circuit.h(q[0]).c_if(c, 15)

                Because cQASM has the same number of classical registers as qubits (2 in this case),
                this circuit cannot be translated to valid cQASM.

        Args:
            experiment: The experiment with gate operations and header.

        Raises:
            QisKitBackendError: When the value is not correct.
        """
        number_of_clbits = experiment.header.memory_slots
        if number_of_clbits < 1:
            raise QisKitBackendError("Invalid amount of classical bits ({})!".format(number_of_clbits))

        if BaseBackend.configuration(self).conditional:
            number_of_qubits = experiment.header.n_qubits
            if number_of_clbits > number_of_qubits:
                # no problem when there are no conditional gate operations
                for instruction in experiment.instructions:
                    if hasattr(instruction, 'conditional'):
                        raise QisKitBackendError("Number of classical bits must be less than or equal to the"
                                                 " number of qubits when using conditional gate operations")
    def from_backend(cls, backend: BaseBackend):
        """Construct an :class:`InstructionDurations` object from the backend.

        Args:
            backend: backend from which durations (gate lengths) and dt are extracted.

        Returns:
            InstructionDurations: The InstructionDurations constructed from backend.

        Raises:
            TranspilerError: If dt and dtm is different in the backend.
        """
        # All durations in seconds in gate_length
        instruction_durations = []
        for gate, insts in backend.properties()._gates.items():
            for qubits, props in insts.items():
                if 'gate_length' in props:
                    gate_length = props['gate_length'][
                        0]  # Throw away datetime at index 1
                    instruction_durations.append(
                        (gate, qubits, gate_length, 's'))

        try:
            dt = backend.configuration().dt  # pylint: disable=invalid-name
        except AttributeError:
            dt = None

        # TODO: backend.properties() should tell us durations of measurements
        # TODO: Remove the following lines after that
        try:
            dtm = backend.configuration().dtm
            if dtm != dt:
                raise TranspilerError("dtm != dt case is not supported.")
            inst_map = backend.defaults().instruction_schedule_map
            all_qubits = tuple(range(backend.configuration().num_qubits))
            meas_duration = inst_map.get('measure', all_qubits).duration
            for q in all_qubits:
                instruction_durations.append(
                    ('measure', [q], meas_duration, 'dt'))
        except AttributeError:
            pass

        return InstructionDurations(instruction_durations, dt=dt)
Esempio n. 9
0
def _tk_gate_set(backend: BaseBackend) -> Set[OpType]:
    """ Set of tket gate types supported by the qiskit backend """
    config = backend.configuration()
    if config.simulator:
        return {
            _gate_str_2_optype[gate_str]
            for gate_str in config.basis_gates
            if gate_str in _gate_str_2_optype
        }.union({OpType.Measure, OpType.Reset, OpType.Barrier})
    else:
        return {
            _gate_str_2_optype[gate_str]
            for gate_str in config.supported_instructions
            if gate_str in _gate_str_2_optype
        }
def create_rb_experiment(rb_seed_circs: List[QuantumCircuit],
                         control: int,
                         target: int,
                         backend: BaseBackend,
                         cnot_sched_control_target: Schedule = None,
                         cnot_sched_target_control: Schedule = None,
                         shots: int = 1024)\
        -> Tuple[List[PulseQobj], List[List[QuantumCircuit]]]:
    """ Create randomized benchmark qobj.

    Args:
        rb_seed_circs: RB circuits.
        control: index of control qubit.
        target: index of target qubit.
        backend: Target quantum system.
        cnot_sched_control_target: Schedule of CX(control, target)
        cnot_sched_target_control: Schedule of CX(target, control)
        shots: Number of shots.
    """
    back_defaults = backend.defaults()
    rb_inst_map = deepcopy(back_defaults.instruction_schedule_map)

    # update circuit instruction map
    if cnot_sched_control_target is not None and cnot_sched_target_control is not None:
        rb_inst_map.add('cx',
                        qubits=(control, target),
                        schedule=cnot_sched_control_target)
        rb_inst_map.add('cx',
                        qubits=(target, control),
                        schedule=cnot_sched_target_control)

    pulse_qobjs = []
    transpiled_circs = []
    for rb_seed_circ in rb_seed_circs:
        # transpile
        rb_seed_circ_transpiled = qiskit.transpile(rb_seed_circ,
                                                   backend,
                                                   optimization_level=0)
        transpiled_circs.append(rb_seed_circ_transpiled)
        # schedule
        rb_seed_sched = qiskit.schedule(rb_seed_circ_transpiled,
                                        backend,
                                        inst_map=rb_inst_map)
        # create pulse qobj
        pulse_qobjs.append(
            qiskit.assemble(rb_seed_sched, backend, meas_level=2, shots=shots))

    return pulse_qobjs, transpiled_circs
def create_qpt_experiment(target_circuits: List[QuantumCircuit],
                          control: int,
                          target: int,
                          backend: BaseBackend,
                          mit_readout: bool = True,
                          inst_map: InstructionScheduleMap = None,
                          basis_gate: List[str] = None,
                          sanity_check: bool = False,
                          shots: int = 2048,
                          return_schedule=False)\
        -> Tuple[Union[PulseQobj, Schedule], List[List[QuantumCircuit]], List[str]]:
    """ Create circuits and schedules for QPT.

    Args:
        target_circuits: List of target circuits for QPT experiment.
        control: index of control qubit.
        target: index of target qubit.
        backend: Target quantum system.
        mit_readout: If use readout mitigation.
        inst_map: instruction mapping object.
        basis_gate: basis gates.
        sanity_check: check memory slot mapping of generated qobj.
        shots: Number of shots.
        return_schedule: set ``True`` when return schedule object instead of qobj.

    Returns:
        Qobj, Schedules, Quantum circuits, Measurement labels

    Additional Information:
        Bit ordering is little endian as a convention of computational science community,
        as the rest of qiskit does. When you measure the CR process tomography of q0 and q1,
        you will observe XZ (ZX) interaction when q0 (q1) is control qubit.
    """
    qubits = sorted([control, target])

    back_config = backend.configuration()
    back_defaults = backend.defaults()

    if inst_map is None:
        inst_map = back_defaults.circuit_instruction_map

    if basis_gate is None:
        basis_gate = back_config.basis_gates

    if isinstance(target_circuits, QuantumCircuit):
        target_circuits = [target_circuits]

    exp_circs = []

    # create the measurement circuits for error mitigation, optional
    qr = target_circuits[0].qregs[0]
    if mit_readout:
        meas_circs, meas_labels = mit.complete_meas_cal(qubit_list=qubits,
                                                        qr=qr,
                                                        circlabel='mcal')
        exp_circs.extend(meas_circs)
    else:
        meas_labels = []

    # create qpt circuit
    qpt_qcs_list = []
    for target_circuit in target_circuits:
        # extract quantum registers from target circuit
        qr = target_circuit.qregs[0]
        qr0 = qr[qubits[0]]
        qr1 = qr[qubits[1]]
        qpt_qcs = tomo.process_tomography_circuits(target_circuit,
                                                   measured_qubits=[qr0, qr1])
        qpt_qcs_list.append(qpt_qcs)
        exp_circs.extend(qpt_qcs)

    # transpile
    exp_circs = qiskit.transpile(exp_circs, backend, basis_gates=basis_gate)

    # schedule with measure alignment
    exp_scheds = align_measures(qiskit.schedule(exp_circs,
                                                backend=backend,
                                                inst_map=inst_map),
                                inst_map=inst_map)

    if return_schedule:
        return exp_scheds, qpt_qcs_list, meas_labels

    # assemble pulse qobj
    qobj = qiskit.assemble(exp_scheds,
                           backend=backend,
                           meas_level=2,
                           shots=shots)

    # sanity check
    if sanity_check:
        for experiment in qobj.experiments:
            for inst in experiment.instructions:
                if inst.name == 'acquire':
                    memory_slot_map = inst.memory_slot
                    if memory_slot_map[qubits[0]] != __reserved_registers[0] or \
                            memory_slot_map[qubits[0]] != __reserved_registers[1]:
                        warnings.warn('Wrong memory slots are assigned. '
                                      'QPT fitter may return invalid result.')

        assert len(qobj.experiments) <= back_config.max_experiments

    return qobj, qpt_qcs_list, meas_labels
Esempio n. 12
0
    def from_calibration_results(cls, backend: BaseBackend,
                                 t1_result: Tuple[np.array, Result],
                                 t2_result: Tuple[np.array,
                                                  Result], meas_result: Result,
                                 zz_results: Dict[Tuple[int, int],
                                                  Tuple[np.array, float,
                                                        Result]]):
        """Takes results from running calibration circuits on hardware and constructs a
        QuacNoiseModel object

        :param backend: the backend on which the circuits were run (a BaseBackend object)
        :param t1_result: a tuple with a list of delay times (in ns) as the 0th element and the T1
            calibration Result object as the 1st element
        :param t2_result: a tuple with a list of delay times (in ns) as the 0th element and the T2
            calibration Result object as the 1st element
        :param meas_result: a Result object from running measurement calibration circuits
        :param zz_results: a dictionary mapping tuples of qubit indices to a ZZ coupling calibration circuit
            Result object
        :return: a QuacNoiseModel object
        """
        n_qubits = len(backend.properties().qubits)
        qubits = list(range(n_qubits))

        # Set up defaults
        t1_times = [float('inf') for _ in range(n_qubits)]
        t2_times = [float('inf') for _ in range(n_qubits)]
        meas_matrices = None
        zz = None

        # Adjust defaults as appropriate
        if t1_result:
            t1_fit = T1Fitter(t1_result[1],
                              t1_result[0],
                              qubits,
                              fit_p0=[1, 1e5, 0],
                              fit_bounds=([0, 0, -1], [2, 1e10, 1]),
                              time_unit="nano-seconds")
            t1_times = t1_fit.time()
        if t2_result:
            t2_fit = T2Fitter(t2_result[1],
                              t2_result[0],
                              qubits,
                              fit_p0=[1, 1e4, 0],
                              fit_bounds=([0, 0, -1], [2, 1e10, 1]),
                              time_unit="nano-seconds")
            t2_times = t2_fit.time()
        if meas_result:
            meas_fit = TensoredMeasFitter(meas_result,
                                          [[qubit] for qubit in qubits])
            meas_matrices = meas_fit.cal_matrices
        if zz_results:
            zz = {}
            for qubit1 in qubits:
                for qubit2 in qubits:
                    if qubit1 < qubit2:
                        zz_information = zz_results[(qubit1, qubit2)]
                        xdata, osc_freq, zz_result = zz_information
                        zz_fit = ZZFitter(
                            zz_result,
                            xdata,
                            [qubit1],
                            [qubit2],
                            fit_p0=[1, osc_freq, -np.pi / 20, 0],
                            fit_bounds=([-0.5, 0, -np.pi,
                                         -0.5], [1.5, 1e10, np.pi, 1.5]),
                        )
                        zz[(qubit1, qubit2)] = zz_fit.ZZ_rate()[0]

        return QuacNoiseModel(t1_times, t2_times, meas_matrices, zz)
Esempio n. 13
0
    def __init__(
            self,
            backend: BaseBackend,
            # run config
            shots: int = 1024,
            seed_simulator: Optional[int] = None,
            max_credits: int = 10,
            # backend properties
            basis_gates: Optional[List[str]] = None,
            coupling_map: Optional[Union[CouplingMap, List[List]]] = None,
            # transpile
            initial_layout: Optional[Union[Layout, Dict, List]] = None,
            pass_manager: Optional[PassManager] = None,
            seed_transpiler: Optional[int] = None,
            optimization_level: Optional[int] = None,
            # simulation
            backend_options: Optional[Dict] = None,
            noise_model: Optional['NoiseModel'] = None,
            # job
            timeout: Optional[float] = None,
            wait: float = 5.,
            # others
            skip_qobj_validation: bool = True,
            measurement_error_mitigation_cls: Optional[Callable] = None,
            cals_matrix_refresh_period: int = 30,
            measurement_error_mitigation_shots: Optional[int] = None,
            job_callback: Optional[Callable] = None) -> None:
        """
        Quantum Instance holds a Qiskit Terra backend as well as configuration for circuit
        transpilation and execution. When provided to an Aqua algorithm the algorithm will
        execute the circuits it needs to run using the instance.

        Args:
            backend: Instance of selected backend
            shots: Number of repetitions of each circuit, for sampling
            seed_simulator: Random seed for simulators
            max_credits: Maximum credits to use
            basis_gates: List of basis gate names supported by the
                                               target. Defaults to basis gates of the backend.
            coupling_map: Coupling map (perhaps custom) to
                                                      target in mapping
            initial_layout: Initial layout of qubits in mapping
            pass_manager: Pass manager to handle how to compile the circuits
            seed_transpiler: The random seed for circuit mapper
            optimization_level: How much optimization to perform on the circuits.
                Higher levels generate more optimized circuits, at the expense of longer
                transpilation time.
            backend_options: All running options for backend, please refer
                to the provider of the backend for information as to what options it supports.
            noise_model: noise model for simulator
            timeout: Seconds to wait for job. If None, wait indefinitely.
            wait: Seconds between queries for job result
            skip_qobj_validation: Bypass Qobj validation to decrease circuit
                processing time during submission to backend.
            measurement_error_mitigation_cls: The approach to mitigate
                measurement errors. Qiskit Ignis provides fitter classes for this functionality
                and CompleteMeasFitter from qiskit.ignis.mitigation.measurement module can be used
                here. (TensoredMeasFitter is not supported).
            cals_matrix_refresh_period: How often to refresh the calibration
                matrix in measurement mitigation. in minutes
            measurement_error_mitigation_shots: The number of shots number for
                building calibration matrix. If None, the main `shots` parameter value is used.
            job_callback: Optional user supplied callback which can be used
                to monitor job progress as jobs are submitted for processing by an Aqua algorithm.
                The callback is provided the following arguments: `job_id, job_status,
                queue_position, job`

        Raises:
            AquaError: the shots exceeds the maximum number of shots
            AquaError: set noise model but the backend does not support that
            AquaError: set backend_options but the backend does not support that
        """
        self._backend = backend
        self._pass_manager = pass_manager

        # setup run config
        if shots is not None:
            if self.is_statevector and shots != 1:
                logger.info(
                    "statevector backend only works with shot=1, changing "
                    "shots from %s to 1.", shots)
                shots = 1
            max_shots = self._backend.configuration().max_shots
            if max_shots is not None and shots > max_shots:
                raise AquaError(
                    'The maximum shots supported by the selected backend is {} '
                    'but you specified {}'.format(max_shots, shots))

        run_config = RunConfig(shots=shots, max_credits=max_credits)
        if seed_simulator:
            run_config.seed_simulator = seed_simulator

        self._run_config = run_config

        # setup backend config
        basis_gates = basis_gates or backend.configuration().basis_gates
        coupling_map = coupling_map or getattr(backend.configuration(),
                                               'coupling_map', None)
        self._backend_config = {
            'basis_gates': basis_gates,
            'coupling_map': coupling_map
        }

        # setup compile config
        self._compile_config = {
            'initial_layout': initial_layout,
            'seed_transpiler': seed_transpiler,
            'optimization_level': optimization_level
        }

        # setup job config
        self._qjob_config = {'timeout': timeout} if self.is_local \
            else {'timeout': timeout, 'wait': wait}

        # setup noise config
        self._noise_config = {}
        if noise_model is not None:
            if is_simulator_backend(
                    self._backend) and not is_basicaer_provider(self._backend):
                self._noise_config = {'noise_model': noise_model}
            else:
                raise AquaError(
                    "The noise model is not supported on the selected backend {} ({}) "
                    "only certain backends, such as Aer qasm simulator "
                    "support noise.".format(self.backend_name,
                                            self._backend.provider()))

        # setup backend options for run
        self._backend_options = {}
        if backend_options is not None:
            if support_backend_options(self._backend):
                self._backend_options = {'backend_options': backend_options}
            else:
                raise AquaError(
                    "backend_options can not used with the backends in IBMQ provider."
                )

        # setup measurement error mitigation
        self._meas_error_mitigation_cls = None
        if self.is_statevector:
            if measurement_error_mitigation_cls is not None:
                raise AquaError("Measurement error mitigation does not work "
                                "with the statevector simulation.")
        else:
            self._meas_error_mitigation_cls = measurement_error_mitigation_cls
        self._meas_error_mitigation_fitters: Dict = {}
        # TODO: support different fitting method in error mitigation?
        self._meas_error_mitigation_method = 'least_squares'
        self._cals_matrix_refresh_period = cals_matrix_refresh_period
        self._meas_error_mitigation_shots = measurement_error_mitigation_shots

        if self._meas_error_mitigation_cls is not None:
            logger.info(
                "The measurement error mitigation is enabled. "
                "It will automatically submit an additional job to help "
                "calibrate the result of other jobs. "
                "The current approach will submit a job with 2^N circuits "
                "to build the calibration matrix, "
                "where N is the number of measured qubits. "
                "Furthermore, Aqua will re-use the calibration matrix for %s minutes "
                "and re-build it after that.",
                self._cals_matrix_refresh_period)

        # setup others
        if is_ibmq_provider(self._backend):
            if skip_qobj_validation:
                logger.info(
                    "skip_qobj_validation was set True but this setting is not "
                    "supported by IBMQ provider and has been ignored.")
                skip_qobj_validation = False
        self._skip_qobj_validation = skip_qobj_validation
        self._circuit_summary = False
        self._job_callback = job_callback
        logger.info(self)
Esempio n. 14
0
def process_characterisation(backend: BaseBackend) -> Dict[str, Any]:
    """Convert a :py:class:`qiskit.BaseBackend` to a dictionary
     containing device Characteristics

    :param backend: A backend to be converted
    :type backend: BaseBackend
    :return: A dictionary containing device characteristics
    :rtype: dict
    """
    gate_set = _tk_gate_set(backend)
    assert OpType.CX in gate_set
    # TODO explicitly check for and separate 1 and 2 qubit gates
    properties = cast("BackendProperties", backend.properties())

    def return_value_if_found(iterator: Iterable["Nduv"],
                              name: str) -> Optional[Any]:
        try:
            first_found = next(filter(lambda item: item.name == name,
                                      iterator))
        except StopIteration:
            return None
        if hasattr(first_found, "value"):
            return first_found.value
        return None

    config = backend.configuration()
    coupling_map = config.coupling_map
    n_qubits = config.n_qubits
    if coupling_map is None:
        # Assume full connectivity
        arc = FullyConnected(n_qubits)
        link_ers_dict = {}
    else:
        arc = Architecture(coupling_map)
        link_ers_dict = {
            tuple(pair): QubitErrorContainer({OpType.CX})
            for pair in coupling_map
        }

    node_ers_dict = {}
    supported_single_optypes = gate_set.difference({OpType.CX})

    t1_times_dict = {}
    t2_times_dict = {}
    frequencies_dict = {}
    gate_times_dict = {}

    if properties is not None:
        for index, qubit_info in enumerate(properties.qubits):
            error_cont = QubitErrorContainer(supported_single_optypes)
            error_cont.add_readout(
                return_value_if_found(qubit_info, "readout_error"))

            t1_times_dict[index] = return_value_if_found(qubit_info, "T1")
            t2_times_dict[index] = return_value_if_found(qubit_info, "T2")
            frequencies_dict[index] = return_value_if_found(
                qubit_info, "frequency")

            node_ers_dict[index] = error_cont

        for gate in properties.gates:
            name = gate.gate
            if name in _gate_str_2_optype:
                optype = _gate_str_2_optype[name]
                qubits = gate.qubits
                gate_error = return_value_if_found(gate.parameters,
                                                   "gate_error")
                gate_error = gate_error if gate_error else 0.0
                gate_length = return_value_if_found(gate.parameters,
                                                    "gate_length")
                gate_length = gate_length if gate_length else 0.0
                gate_times_dict[(optype, tuple(qubits))] = gate_length
                # add gate fidelities to their relevant lists
                if len(qubits) == 1:
                    node_ers_dict[qubits[0]].add_error((optype, gate_error))
                elif len(qubits) == 2:
                    link_ers_dict[tuple(qubits)].add_error(
                        (optype, gate_error))
                    opposite_link = tuple(qubits[::-1])
                    if opposite_link not in coupling_map:
                        # to simulate a worse reverse direction square the fidelity
                        link_ers_dict[opposite_link] = QubitErrorContainer(
                            {OpType.CX})
                        link_ers_dict[opposite_link].add_error(
                            (optype, 2 * gate_error))

    # convert qubits to architecture Nodes
    node_ers_dict = {
        Node(q_index): ers
        for q_index, ers in node_ers_dict.items()
    }
    link_ers_dict = {(Node(q_indices[0]), Node(q_indices[1])): ers
                     for q_indices, ers in link_ers_dict.items()}

    characterisation: Dict[str, Any] = dict()
    characterisation["NodeErrors"] = node_ers_dict
    characterisation["EdgeErrors"] = link_ers_dict
    characterisation["Architecture"] = arc
    characterisation["t1times"] = t1_times_dict
    characterisation["t2times"] = t2_times_dict
    characterisation["Frequencies"] = frequencies_dict
    characterisation["GateTimes"] = gate_times_dict

    return characterisation