def test_qubits_with_instruction(self):
        """Test `qubits_with_instruction`."""
        sched = Schedule()
        inst_map = InstructionScheduleMap()

        inst_map.add('u1', (0,), sched)
        inst_map.add('u1', (1,), sched)
        inst_map.add('cx', [0, 1], sched)

        self.assertEqual(inst_map.qubits_with_instruction('u1'), [0, 1])
        self.assertEqual(inst_map.qubits_with_instruction('cx'), [(0, 1)])
        self.assertEqual(inst_map.qubits_with_instruction('none'), [])
Пример #2
0
    def test_parameterized_schedule(self):
        """Test adding parameterized schedule."""
        converter = QobjToInstructionConverter([], buffer=0)
        qobj = PulseQobjInstruction(name='pv',
                                    ch='u1',
                                    t0=10,
                                    val='P2*cos(np.pi*P1)')
        converted_instruction = converter(qobj)

        inst_map = InstructionScheduleMap()

        inst_map.add('pv_test', 0, converted_instruction)
        self.assertEqual(inst_map.get_parameters('pv_test', 0), ('P1', 'P2'))

        sched = inst_map.get('pv_test', 0, P1=0, P2=-1)
        self.assertEqual(sched.instructions[0][-1].command.value, -1)
        with self.assertRaises(PulseError):
            inst_map.get('pv_test', 0, 0, P1=-1)
        with self.assertRaises(PulseError):
            inst_map.get('pv_test', 0, P1=1, P2=2, P3=3)
Пример #3
0
    def test_schedule_block_in_instmap(self):
        """Test schedule block in instmap can be scheduled."""
        duration = Parameter("duration")

        with build() as pulse_prog:
            play(Gaussian(duration, 0.1, 10), DriveChannel(0))

        instmap = InstructionScheduleMap()
        instmap.add("block_gate", (0, ), pulse_prog, ["duration"])

        qc = QuantumCircuit(1)
        qc.append(Gate("block_gate", 1, [duration]), [0])
        qc.assign_parameters({duration: 100}, inplace=True)

        sched = schedule(qc, self.backend, inst_map=instmap)

        ref_sched = Schedule()
        ref_sched += Play(Gaussian(100, 0.1, 10), DriveChannel(0))

        self.assertEqual(sched, ref_sched)
    def test_get_schedule_with_unbound_parameter(self):
        """Test get schedule with partial binding."""
        param1 = Parameter("param1")
        param2 = Parameter("param2")

        target_sched = Schedule()
        target_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True)
        target_sched.insert(10, ShiftPhase(param2, DriveChannel(0)), inplace=True)

        inst_map = InstructionScheduleMap()
        inst_map.add("target_sched", (0,), target_sched)

        ref_sched = Schedule()
        ref_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True)
        ref_sched.insert(10, ShiftPhase(1.23, DriveChannel(0)), inplace=True)

        test_sched = inst_map.get("target_sched", (0,), param2=1.23)

        for test_inst, ref_inst in zip(test_sched.instructions, ref_sched.instructions):
            self.assertEqual(test_inst[0], ref_inst[0])
            self.assertAlmostEqual(test_inst[1], ref_inst[1])
Пример #5
0
    def test_schedule_generator_supports_parameter_expressions(self):
        """Test expression-based schedule generator functionalty."""

        t_param = Parameter('t')
        amp = 1.0

        def test_func(dur: ParameterExpression, t_val: int):
            dur_bound = dur.bind({t_param: t_val})
            sched = Schedule()
            sched += Play(library.constant(int(float(dur_bound)), amp),
                          DriveChannel(0))
            return sched

        expected_sched = Schedule()
        expected_sched += Play(library.constant(10, amp), DriveChannel(0))

        inst_map = InstructionScheduleMap()
        inst_map.add('f', (0, ), test_func)
        self.assertEqual(inst_map.get('f', (0, ), dur=2 * t_param, t_val=5),
                         expected_sched)

        self.assertEqual(inst_map.get_parameters('f', (0, )), (
            'dur',
            't_val',
        ))
Пример #6
0
    def test_schedule_with_non_alphanumeric_ordering(self):
        """Test adding and getting schedule with non obvious parameter ordering."""
        theta = Parameter('theta')
        phi = Parameter('phi')
        lamb = Parameter('lambda')

        target_sched = Schedule()
        target_sched.insert(0,
                            ShiftPhase(theta, DriveChannel(0)),
                            inplace=True)
        target_sched.insert(10, ShiftPhase(phi, DriveChannel(0)), inplace=True)
        target_sched.insert(20,
                            ShiftPhase(lamb, DriveChannel(0)),
                            inplace=True)

        inst_map = InstructionScheduleMap()
        inst_map.add('target_sched', (0, ),
                     target_sched,
                     arguments=['theta', 'phi', 'lambda'])

        ref_sched = Schedule()
        ref_sched.insert(0, ShiftPhase(0, DriveChannel(0)), inplace=True)
        ref_sched.insert(10, ShiftPhase(1, DriveChannel(0)), inplace=True)
        ref_sched.insert(20, ShiftPhase(2, DriveChannel(0)), inplace=True)

        # if parameter is alphanumerical ordering this maps to
        # theta -> 2
        # phi -> 1
        # lamb -> 0
        # however non alphanumerical ordering is specified in add method thus mapping should be
        # theta -> 0
        # phi -> 1
        # lamb -> 2
        test_sched = inst_map.get('target_sched', (0, ), 0, 1, 2)

        for test_inst, ref_inst in zip(test_sched.instructions,
                                       ref_sched.instructions):
            self.assertEqual(test_inst[0], ref_inst[0])
            self.assertEqual(test_inst[1], ref_inst[1])
Пример #7
0
    def test_circuits(self):
        """Test that transpiling works and that we can have a y gate with a calibration."""

        qubit = 1

        inst_map = InstructionScheduleMap()
        for inst in ["sx", "y"]:
            inst_map.add(inst, (qubit,), pulse.Schedule(name=inst))

        hac = HalfAngle(qubit)
        hac.set_transpile_options(inst_map=inst_map)

        # mimic what will happen in the experiment.
        transpile_opts = copy.copy(hac.transpile_options.__dict__)
        transpile_opts["initial_layout"] = list(hac._physical_qubits)
        circuits = transpile(hac.circuits(), FakeAthens(), **transpile_opts)

        for idx, circ in enumerate(circuits):
            self.assertEqual(circ.count_ops()["sx"], idx * 2 + 2)
            self.assertEqual(circ.calibrations["sx"][((qubit,), ())], pulse.Schedule(name="sx"))
            if idx > 0:
                self.assertEqual(circ.count_ops()["y"], idx)
                self.assertEqual(circ.calibrations["y"][((qubit,), ())], pulse.Schedule(name="y"))
    def test_schedule_with_multiple_parameters_under_same_name(self):
        """Test getting schedule with parameters that have the same name."""
        param1 = Parameter("param")
        param2 = Parameter("param")
        param3 = Parameter("param")

        target_sched = Schedule()
        target_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True)
        target_sched.insert(10, ShiftPhase(param2, DriveChannel(0)), inplace=True)
        target_sched.insert(20, ShiftPhase(param3, DriveChannel(0)), inplace=True)

        inst_map = InstructionScheduleMap()
        inst_map.add("target_sched", (0,), target_sched)

        ref_sched = Schedule()
        ref_sched.insert(0, ShiftPhase(1.23, DriveChannel(0)), inplace=True)
        ref_sched.insert(10, ShiftPhase(1.23, DriveChannel(0)), inplace=True)
        ref_sched.insert(20, ShiftPhase(1.23, DriveChannel(0)), inplace=True)

        test_sched = inst_map.get("target_sched", (0,), param=1.23)

        for test_inst, ref_inst in zip(test_sched.instructions, ref_sched.instructions):
            self.assertEqual(test_inst[0], ref_inst[0])
            self.assertAlmostEqual(test_inst[1], ref_inst[1])
Пример #9
0
    def test_instructions_gate(self):
        """Test `instructions`."""
        sched = Schedule()
        inst_map = InstructionScheduleMap()

        inst_map.add(U1Gate(0), 1, sched)
        inst_map.add(U3Gate(0, 0, 0), 0, sched)

        instructions = inst_map.instructions
        for inst in ['u1', 'u3']:
            self.assertTrue(inst in instructions)
    def test_binding_unassigned_parameters(self):
        """Test getting schedule with unassigned parameter binding."""
        param = Parameter("param")

        target_sched = Schedule()
        target_sched.insert(0, ShiftPhase(param, DriveChannel(0)), inplace=True)

        inst_map = InstructionScheduleMap()
        inst_map.add("target_sched", (0,), target_sched)

        with self.assertRaises(PulseError):
            inst_map.get("target_sched", (0,), P0=0)
    def test_add_block(self):
        """Test add block, and that errors are raised when expected."""
        sched = ScheduleBlock()
        sched.append(Play(Waveform(np.ones(5)), DriveChannel(0)), inplace=True)
        inst_map = InstructionScheduleMap()

        inst_map.add("u1", 1, sched)
        inst_map.add("u1", 0, sched)

        self.assertIn("u1", inst_map.instructions)
        self.assertEqual(inst_map.qubits_with_instruction("u1"), [0, 1])
        self.assertTrue("u1" in inst_map.qubit_instructions(0))
    def test_binding_too_many_parameters(self):
        """Test getting schedule with too many parameter binding."""
        param = Parameter('param')

        target_sched = Schedule()
        target_sched.insert(0, ShiftPhase(param, DriveChannel(0)), inplace=True)

        inst_map = InstructionScheduleMap()
        inst_map.add('target_sched', (0,), target_sched)

        with self.assertRaises(PulseError):
            inst_map.get('target_sched', (0,), 0, 1, 2, 3)
    def test_partially_bound_callable(self):
        """Test register partial function."""
        import functools

        def callable_schedule(par_b, par_a):
            sched = Schedule()
            sched.insert(10,
                         Play(Constant(10, par_b), DriveChannel(0)),
                         inplace=True)
            sched.insert(20,
                         Play(Constant(10, par_a), DriveChannel(0)),
                         inplace=True)
            return sched

        ref_sched = Schedule()
        ref_sched.insert(10,
                         Play(Constant(10, 0.1), DriveChannel(0)),
                         inplace=True)
        ref_sched.insert(20,
                         Play(Constant(10, 0.2), DriveChannel(0)),
                         inplace=True)

        inst_map = InstructionScheduleMap()

        def test_callable_sched1(par_b):
            return callable_schedule(par_b, 0.2)

        inst_map.add("my_gate1", (0, ), test_callable_sched1, ["par_b"])
        ret_sched = inst_map.get("my_gate1", (0, ), par_b=0.1)
        self.assertEqual(ret_sched, ref_sched)

        # bind partially
        test_callable_sched2 = functools.partial(callable_schedule, par_a=0.2)

        inst_map.add("my_gate2", (0, ), test_callable_sched2, ["par_b"])
        ret_sched = inst_map.get("my_gate2", (0, ), par_b=0.1)
        self.assertEqual(ret_sched, ref_sched)
Пример #14
0
    def test_schedule_generator(self):
        """Test schedule generator functionalty."""

        x_test = 10
        amp_test = 1.0

        def test_func(x):
            sched = Schedule()
            sched += Play(library.constant(int(x), amp_test), DriveChannel(0))
            return sched

        ref_sched = Schedule()
        ref_sched += Play(library.constant(x_test, amp_test), DriveChannel(0))

        inst_map = InstructionScheduleMap()
        inst_map.add('f', (0,), test_func)
        self.assertEqual(inst_map.get('f', (0,), x_test), ref_sched)

        self.assertEqual(inst_map.get_parameters('f', (0,)), ('x',))
Пример #15
0
    def test_schedule_generator(self):
        """Test schedule generator functionalty."""

        dur_val = 10
        amp = 1.0

        def test_func(dur: int):
            sched = Schedule()
            sched += Play(library.constant(int(dur), amp), DriveChannel(0))
            return sched

        expected_sched = Schedule()
        expected_sched += Play(library.constant(dur_val, amp), DriveChannel(0))

        inst_map = InstructionScheduleMap()
        inst_map.add('f', (0, ), test_func)
        self.assertEqual(inst_map.get('f', (0, ), dur_val), expected_sched)

        self.assertEqual(inst_map.get_parameters('f', (0, )), ('dur', ))
    def test_has_gate(self):
        """Test `has` and `assert_has`."""
        sched = Schedule()
        inst_map = InstructionScheduleMap()

        inst_map.add(U1Gate(0), (0,), sched)
        inst_map.add(CXGate(), [0, 1], sched)

        self.assertTrue(inst_map.has(U1Gate(0), [0]))
        self.assertTrue(inst_map.has(CXGate(), (0, 1)))
        with self.assertRaises(PulseError):
            inst_map.assert_has("dne", [0])
        with self.assertRaises(PulseError):
            inst_map.assert_has(CXGate(), 100)
Пример #17
0
    def test_has(self):
        """Test `has` and `assert_has`."""
        sched = Schedule()
        inst_map = InstructionScheduleMap()

        inst_map.add('u1', (0, ), sched)
        inst_map.add('cx', [0, 1], sched)

        self.assertTrue(inst_map.has('u1', [0]))
        self.assertTrue(inst_map.has('cx', (0, 1)))
        with self.assertRaises(PulseError):
            inst_map.assert_has('dne', [0])
        with self.assertRaises(PulseError):
            inst_map.assert_has('cx', 100)
Пример #18
0
    def test_sequenced_parameterized_schedule(self):
        """Test parametrized schedule consists of multiple instruction. """
        converter = QobjToInstructionConverter([], buffer=0)
        qobjs = [
            PulseQobjInstruction(name='fc', ch='d0', t0=10, phase='P1'),
            PulseQobjInstruction(name='fc', ch='d0', t0=20, phase='P2'),
            PulseQobjInstruction(name='fc', ch='d0', t0=30, phase='P3')
        ]
        converted_instruction = [converter(qobj) for qobj in qobjs]

        inst_map = InstructionScheduleMap()

        inst_map.add(
            'inst_seq', 0,
            ParameterizedSchedule(*converted_instruction, name='inst_seq'))

        with self.assertRaises(PulseError):
            inst_map.get('inst_seq', 0, P1=1, P2=2, P3=3, P4=4, P5=5)

        with self.assertRaises(PulseError):
            inst_map.get('inst_seq', 0, P1=1)

        with self.assertRaises(PulseError):
            inst_map.get('inst_seq', 0, 1, 2, 3, P1=1)

        sched = inst_map.get('inst_seq', 0, 1, 2, 3)
        self.assertEqual(sched.instructions[0][-1].command.phase, 1)
        self.assertEqual(sched.instructions[1][-1].command.phase, 2)
        self.assertEqual(sched.instructions[2][-1].command.phase, 3)

        sched = inst_map.get('inst_seq', 0, P1=1, P2=2, P3=3)
        self.assertEqual(sched.instructions[0][-1].command.phase, 1)
        self.assertEqual(sched.instructions[1][-1].command.phase, 2)
        self.assertEqual(sched.instructions[2][-1].command.phase, 3)

        sched = inst_map.get('inst_seq', 0, 1, 2, P3=3)
        self.assertEqual(sched.instructions[0][-1].command.phase, 1)
        self.assertEqual(sched.instructions[1][-1].command.phase, 2)
        self.assertEqual(sched.instructions[2][-1].command.phase, 3)
Пример #19
0
class BackendCalibrations(Calibrations):
    """
    A Calibrations class to enable a seamless interplay with backend objects.
    This class enables users to export their calibrations into a backend object.
    Additionally, it creates frequency parameters for qubits and readout resonators.
    The parameters are named `qubit_lo_freq` and `meas_lo_freq` to be consistent
    with the naming in backend.defaults(). These two parameters are not attached to
    any schedule.
    """

    __qubit_freq_parameter__ = "qubit_lo_freq"
    __readout_freq_parameter__ = "meas_lo_freq"

    def __init__(
        self,
        backend: Backend,
        library: BasisGateLibrary = None,
        num_qubits: Optional[int] = None,
    ):
        """Setup an instance to manage the calibrations of a backend.

        BackendCalibrations can be initialized from a basis gate library, i.e. a subclass of
        :class:`BasisGateLibrary`. As example consider the following code:

        .. code-block:: python

            cals = BackendCalibrations(
                    backend,
                    library=FixedFrequencyTransmon(
                        basis_gates=["x", "sx"],
                        default_values={duration: 320}
                    )
                )

        Args:
            backend: A backend instance from which to extract the qubit and readout frequencies
                (which will be added as first guesses for the corresponding parameters) as well
                as the coupling map.
            library: A library class that will be instantiated with the library options to then
                get template schedules to register as well as default parameter values.
            num_qubits: Number of qubits in case the backend object fails to specify this in its
                configuration.

        Raises:
            CalibrationError: If the backend configuration does not have num_qubits and num_qubits
                is None.
        """
        super().__init__(
            getattr(backend.configuration(), "control_channels", None))

        # Instruction schedule map variables and support variables.
        self._inst_map = InstructionScheduleMap()
        self._operated_qubits = defaultdict(list)
        self._update_inst_map = False  # When True add_parameter_value triggers an inst. map update

        # Use the same naming convention as in backend.defaults()
        self.qubit_freq = Parameter(self.__qubit_freq_parameter__)
        self.meas_freq = Parameter(self.__readout_freq_parameter__)
        self._register_parameter(self.qubit_freq, ())
        self._register_parameter(self.meas_freq, ())

        num_qubits = getattr(backend.configuration(), "num_qubits", num_qubits)
        if num_qubits is None:
            raise CalibrationError(
                "backend.configuration() does not have 'num_qubits' and None given."
            )

        self._qubits = list(range(num_qubits))
        self._backend = backend

        for qubit, freq in enumerate(backend.defaults().qubit_freq_est):
            self.add_parameter_value(freq, self.qubit_freq, qubit)

        for meas, freq in enumerate(backend.defaults().meas_freq_est):
            self.add_parameter_value(freq, self.meas_freq, meas)

        if library is not None:

            # Add the basis gates
            for gate in library.basis_gates:
                self.add_schedule(library[gate],
                                  num_qubits=library.num_qubits(gate))

            # Add the default values
            for param_conf in library.default_values():
                schedule_name = param_conf[-1]
                if schedule_name in library.basis_gates:
                    self.add_parameter_value(*param_conf)

        self._update_inst_map = True

        # Push the schedules to the instruction schedule map.
        self.update_inst_map()

    @property
    def default_inst_map(self) -> InstructionScheduleMap:
        """Return the default and up to date instruction schedule map."""
        return self._inst_map

    def get_inst_map(
        self,
        group: str = "default",
        cutoff_date: datetime = None,
    ) -> InstructionScheduleMap:
        """Get a new instance of an Instruction schedule map.

        Args:
            group: The calibration group from which to draw the parameters.
                If not specified this defaults to the 'default' group.
            cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters
                generated after the cutoff date will be ignored. If the cutoff_date is None then
                all parameters are considered. This allows users to discard more recent values that
                may be erroneous.

        Returns:
            An instruction schedule map with parameters updated up to the desired cutoff date
            and from the desired calibration group.
        """
        inst_map = InstructionScheduleMap()

        self.update_inst_map(group=group,
                             cutoff_date=cutoff_date,
                             inst_map=inst_map)

        return inst_map

    def _get_frequencies(
        self,
        element: FrequencyElement,
        group: str = "default",
        cutoff_date: datetime = None,
    ) -> List[float]:
        """Internal helper method."""

        if element == FrequencyElement.READOUT:
            param = self.meas_freq.name
        elif element == FrequencyElement.QUBIT:
            param = self.qubit_freq.name
        else:
            raise CalibrationError(
                f"Frequency element {element} is not supported.")

        freqs = []
        for qubit in self._qubits:
            schedule = None  # A qubit frequency is not attached to a schedule.
            if ParameterKey(param, (qubit, ), schedule) in self._params:
                freq = self.get_parameter_value(param, (qubit, ), schedule,
                                                True, group, cutoff_date)
            else:
                if element == FrequencyElement.READOUT:
                    freq = self._backend.defaults().meas_freq_est[qubit]
                elif element == FrequencyElement.QUBIT:
                    freq = self._backend.defaults().qubit_freq_est[qubit]
                else:
                    raise CalibrationError(
                        f"Frequency element {element} is not supported.")

            freqs.append(freq)

        return freqs

    def get_qubit_frequencies(
        self,
        group: str = "default",
        cutoff_date: datetime = None,
    ) -> List[float]:
        """
        Get the most recent qubit frequencies. They can be passed to the run-time
        options of :class:`BaseExperiment`. If no calibrated frequency value of a
        qubit is found then the default value from the backend defaults is used.
        Only valid parameter values are returned.

        Args:
            group: The calibration group from which to draw the
                parameters. If not specified, this defaults to the 'default' group.
            cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters
                generated after the cutoff date will be ignored. If the cutoff_date is None then
                all parameters are considered. This allows users to discard more recent values
                that may be erroneous.

        Returns:
            A List of qubit frequencies for all qubits of the backend.
        """
        return self._get_frequencies(FrequencyElement.QUBIT, group,
                                     cutoff_date)

    def get_meas_frequencies(
        self,
        group: str = "default",
        cutoff_date: datetime = None,
    ) -> List[float]:
        """
        Get the most recent measurement frequencies. They can be passed to the run-time
        options of :class:`BaseExperiment`. If no calibrated frequency value of a
        measurement is found then the default value from the backend defaults is used.
        Only valid parameter values are returned.

        Args:
            group: The calibration group from which to draw the
                parameters. If not specified, this defaults to the 'default' group.
            cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters
                generated after the cutoff date will be ignored. If the cutoff_date is None then
                all parameters are considered. This allows users to discard more recent values
                that may be erroneous.

        Returns:
            A List of measurement frequencies for all qubits of the backend.
        """
        return self._get_frequencies(FrequencyElement.READOUT, group,
                                     cutoff_date)

    def export_backend(self) -> Backend:
        """
        Exports the calibrations to a backend object that can be used.

        Returns:
            calibrated backend: A backend with the calibrations in it.
        """
        backend = copy.deepcopy(self._backend)

        backend.defaults().qubit_freq_est = self.get_qubit_frequencies()
        backend.defaults().meas_freq_est = self.get_meas_frequencies()
        backend.default().instruction_schedule_map = self._inst_map

        return backend

    def inst_map_add(
        self,
        instruction_name: str,
        qubits: Tuple[int],
        schedule_name: Optional[str] = None,
        assign_params: Optional[Dict[Union[str, ParameterKey],
                                     ParameterValueType]] = None,
    ):
        """Update a single instruction in the instruction schedule map.

        This method can be used to update a single instruction for the given qubits but
        it can also be used by experiments that define custom gates with parameters
        such as the :class:`Rabi` experiment. In a Rabi experiment there is a gate named
        "Rabi" that scans a pulse with a custom amplitude. Therefore we would do

        .. code-block:: python

            cals.inst_map_add("Rabi", (0, ), "xp", assign_params={"amp": Parameter("amp")})

        to temporarily add a pulse for the Rabi gate in the instruction schedule map. This
        then allows calling :code:`transpile(circ, inst_map=cals.instruction_schedule_map)`.

        Args:
            instruction_name: The name of the instruction to add to the instruction schedule map.
            qubits: The qubits to which the instruction will apply.
            schedule_name: The name of the schedule. If None is given then we assume that the
                schedule and the instruction have the same name.
            assign_params: An optional dict of parameter mappings to apply. See for instance
                :meth:`get_schedule` of :class:`Calibrations`.
        """
        schedule_name = schedule_name or instruction_name

        inst_map_args = None
        if assign_params is not None:
            inst_map_args = assign_params.keys()

        self._inst_map.add(
            instruction=instruction_name,
            qubits=qubits,
            schedule=self.get_schedule(schedule_name, qubits, assign_params),
            arguments=inst_map_args,
        )

    def update_inst_map(
        self,
        schedules: Optional[set] = None,
        qubits: Optional[Tuple[int]] = None,
        group: Optional[str] = "default",
        cutoff_date: datetime = None,
        inst_map: Optional[InstructionScheduleMap] = None,
    ):
        """Push all schedules from the Calibrations to the inst map.

        This will create instructions with the same name as the schedules.

        Args:
            schedules: The name of the schedules to update. If None is given then
                all schedules will be pushed to instructions.
            qubits: The qubits for which to update the instruction schedule map.
                If qubits is None then all possible schedules defined by the coupling
                map will be updated.
            group: The calibration group from which to draw the parameters. If not specified
                this defaults to the 'default' group.
            cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters
                generated after the cutoff date will be ignored. If the cutoff_date is None then
                all parameters are considered. This allows users to discard more recent values that
                may be erroneous.
            inst_map: The instruction schedule map to update. If None is given then the default
                instruction schedule map (i.e. self._inst_map) will be updated.
        """
        inst_map = inst_map or self._inst_map

        for key in self._schedules:
            sched_name = key.schedule

            if schedules is not None and sched_name not in schedules:
                continue

            if qubits is not None:
                inst_map.add(
                    instruction=sched_name,
                    qubits=qubits,
                    schedule=self.get_schedule(sched_name,
                                               qubits,
                                               group=group,
                                               cutoff_date=cutoff_date),
                )

            else:
                for qubits_ in self.operated_qubits[
                        self._schedules_qubits[key]]:
                    try:
                        inst_map.add(
                            instruction=sched_name,
                            qubits=qubits_,
                            schedule=self.get_schedule(
                                sched_name,
                                qubits_,
                                group=group,
                                cutoff_date=cutoff_date),
                        )
                    except CalibrationError:
                        # get_schedule may raise an error if not all parameters have values or
                        # default values. In this case we ignore and continue updating inst_map.
                        pass

    def _parameter_inst_map_update(self, param: Parameter):
        """Update all instructions in the inst map that contain the given parameter."""

        schedules = set(key.schedule for key in self._parameter_map_r[param])

        self.update_inst_map(schedules)

    def add_parameter_value(
        self,
        value: Union[int, float, complex, ParameterValue],
        param: Union[Parameter, str],
        qubits: Union[int, Tuple[int, ...]] = None,
        schedule: Union[ScheduleBlock, str] = None,
    ):
        """Add a parameter value to the stored parameters.

        This parameter value may be applied to several channels, for instance, all
        DRAG pulses may have the same standard deviation. Additionally, this function
        will update any instructions in the instruction schedule map that contain this
        parameter.

        Args:
            value: The value of the parameter to add. If an int, float, or complex is given
                then the timestamp of the parameter value will automatically be generated
                and set to the current local time of the user.
            param: The parameter or its name for which to add the measured value.
            qubits: The qubits to which this parameter applies.
            schedule: The schedule or its name for which to add the measured parameter value.
        """
        super().add_parameter_value(value, param, qubits, schedule)

        if self._update_inst_map:
            if schedule is not None:
                schedule = schedule.name if isinstance(
                    schedule, ScheduleBlock) else schedule
                param_obj = self.calibration_parameter(param, qubits, schedule)
                self._parameter_inst_map_update(param_obj)

    @property
    def operated_qubits(self) -> Dict[int, List[int]]:
        """Get a dict describing qubit couplings.

        This is an extension of the coupling map and used as a convenience to help populate
        the instruction schedule map.

        Returns:
            A dict where the key is the number of qubits coupled and the value is a list of
            lists where the sublist shows which qubits are coupled. For example, a three qubit
            system with a three qubit gate and three two-qubit gates would be represented as

            .. parsed-literal::

                {
                    1: [[0], [1], [2]],
                    2: [[0, 1], [1, 2], [2, 1]],
                    3: [[0, 1, 2]]
                }
        """

        # Use the cached map if there is one.
        if len(self._operated_qubits) != 0:
            return self._operated_qubits

        # Single qubits
        for qubit in self._qubits:
            self._operated_qubits[1].append([qubit])

        # Multi-qubit couplings
        if self._backend.configuration().coupling_map is not None:
            for coupling in self._backend.configuration().coupling_map:
                self._operated_qubits[len(coupling)].append(coupling)

        return self._operated_qubits
Пример #20
0
    def __init__(
        self,
        backend: Backend,
        library: BasisGateLibrary = None,
        num_qubits: Optional[int] = None,
    ):
        """Setup an instance to manage the calibrations of a backend.

        BackendCalibrations can be initialized from a basis gate library, i.e. a subclass of
        :class:`BasisGateLibrary`. As example consider the following code:

        .. code-block:: python

            cals = BackendCalibrations(
                    backend,
                    library=FixedFrequencyTransmon(
                        basis_gates=["x", "sx"],
                        default_values={duration: 320}
                    )
                )

        Args:
            backend: A backend instance from which to extract the qubit and readout frequencies
                (which will be added as first guesses for the corresponding parameters) as well
                as the coupling map.
            library: A library class that will be instantiated with the library options to then
                get template schedules to register as well as default parameter values.
            num_qubits: Number of qubits in case the backend object fails to specify this in its
                configuration.

        Raises:
            CalibrationError: If the backend configuration does not have num_qubits and num_qubits
                is None.
        """
        super().__init__(
            getattr(backend.configuration(), "control_channels", None))

        # Instruction schedule map variables and support variables.
        self._inst_map = InstructionScheduleMap()
        self._operated_qubits = defaultdict(list)
        self._update_inst_map = False  # When True add_parameter_value triggers an inst. map update

        # Use the same naming convention as in backend.defaults()
        self.qubit_freq = Parameter(self.__qubit_freq_parameter__)
        self.meas_freq = Parameter(self.__readout_freq_parameter__)
        self._register_parameter(self.qubit_freq, ())
        self._register_parameter(self.meas_freq, ())

        num_qubits = getattr(backend.configuration(), "num_qubits", num_qubits)
        if num_qubits is None:
            raise CalibrationError(
                "backend.configuration() does not have 'num_qubits' and None given."
            )

        self._qubits = list(range(num_qubits))
        self._backend = backend

        for qubit, freq in enumerate(backend.defaults().qubit_freq_est):
            self.add_parameter_value(freq, self.qubit_freq, qubit)

        for meas, freq in enumerate(backend.defaults().meas_freq_est):
            self.add_parameter_value(freq, self.meas_freq, meas)

        if library is not None:

            # Add the basis gates
            for gate in library.basis_gates:
                self.add_schedule(library[gate],
                                  num_qubits=library.num_qubits(gate))

            # Add the default values
            for param_conf in library.default_values():
                schedule_name = param_conf[-1]
                if schedule_name in library.basis_gates:
                    self.add_parameter_value(*param_conf)

        self._update_inst_map = True

        # Push the schedules to the instruction schedule map.
        self.update_inst_map()
    def test_sequenced_parameterized_schedule(self):
        """Test parameterized schedule consists of multiple instruction."""

        converter = QobjToInstructionConverter([], buffer=0)
        qobjs = [
            PulseQobjInstruction(name="fc", ch="d0", t0=10, phase="P1"),
            PulseQobjInstruction(name="fc", ch="d0", t0=20, phase="P2"),
            PulseQobjInstruction(name="fc", ch="d0", t0=30, phase="P3"),
        ]
        converted_instruction = [converter(qobj) for qobj in qobjs]

        inst_map = InstructionScheduleMap()

        inst_map.add("inst_seq", 0,
                     Schedule(*converted_instruction, name="inst_seq"))

        with self.assertRaises(PulseError):
            inst_map.get("inst_seq", 0, P1=1, P2=2, P3=3, P4=4, P5=5)

        with self.assertRaises(PulseError):
            inst_map.get("inst_seq", 0, 1, 2, 3, 4, 5, 6, 7, 8)

        p3_expr = Parameter("p3")
        p3_expr = p3_expr.bind({p3_expr: 3})

        sched = inst_map.get("inst_seq", 0, 1, 2, p3_expr)
        self.assertEqual(sched.instructions[0][-1].phase, 1)
        self.assertEqual(sched.instructions[1][-1].phase, 2)
        self.assertEqual(sched.instructions[2][-1].phase, 3)

        sched = inst_map.get("inst_seq", 0, P1=1, P2=2, P3=p3_expr)
        self.assertEqual(sched.instructions[0][-1].phase, 1)
        self.assertEqual(sched.instructions[1][-1].phase, 2)
        self.assertEqual(sched.instructions[2][-1].phase, 3)

        sched = inst_map.get("inst_seq", 0, 1, 2, P3=p3_expr)
        self.assertEqual(sched.instructions[0][-1].phase, 1)
        self.assertEqual(sched.instructions[1][-1].phase, 2)
        self.assertEqual(sched.instructions[2][-1].phase, 3)