def convert_parametric(self, instruction):
        """Return the ParametricPulse implementation that is described by the instruction.

        If parametric pulse label is not provided by the backend, this method naively generates
        a pulse name based on the pulse shape and bound parameters. This pulse name is formatted
        to, for example, `gaussian_a4e3`, here the last four digits are a part of
        the hash string generated based on the pulse shape and the parameters.
        Because we are using a truncated hash for readability,
        there may be a small risk of pulse name collision with other pulses.
        Basically the parametric pulse name is used just for visualization purpose and
        the pulse module should not have dependency on the parametric pulse names.

        Args:
            instruction (PulseQobjInstruction): pulse qobj
        Returns:
            Schedule: Schedule containing the converted pulse
        """
        t0 = instruction.t0
        channel = self.get_channel(instruction.ch)

        try:
            pulse_name = instruction.label
        except AttributeError:
            sorted_params = sorted(tuple(instruction.parameters.items()), key=lambda x: x[0])
            base_str = "{pulse}_{params}".format(
                pulse=instruction.pulse_shape, params=str(sorted_params)
            )
            short_pulse_id = hashlib.md5(base_str.encode("utf-8")).hexdigest()[:4]
            pulse_name = f"{instruction.pulse_shape}_{short_pulse_id}"

        pulse = ParametricPulseShapes[instruction.pulse_shape].value(
            **instruction.parameters, name=pulse_name
        )
        return instructions.Play(pulse, channel) << t0
Esempio n. 2
0
def compress_pulses(schedules: List[Schedule]) -> List[Schedule]:
    """Optimization pass to replace identical pulses.

    Args:
        schedules: Schedules to compress.

    Returns:
        Compressed schedules.
    """
    existing_pulses = []
    new_schedules = []

    for schedule in schedules:
        new_schedule = Schedule.initialize_from(schedule)

        for time, inst in schedule.instructions:
            if isinstance(inst, instructions.Play):
                if inst.pulse in existing_pulses:
                    idx = existing_pulses.index(inst.pulse)
                    identical_pulse = existing_pulses[idx]
                    new_schedule.insert(
                        time,
                        instructions.Play(identical_pulse, inst.channel,
                                          inst.name),
                        inplace=True,
                    )
                else:
                    existing_pulses.append(inst.pulse)
                    new_schedule.insert(time, inst, inplace=True)
            else:
                new_schedule.insert(time, inst, inplace=True)

        new_schedules.append(new_schedule)

    return new_schedules
Esempio n. 3
0
    def test_play(self):
        """Test basic play instruction."""
        play = instructions.Play(self.pulse_op, channels.DriveChannel(1))

        self.assertIsInstance(play.id, int)
        self.assertEqual(play.name, self.pulse_op.name)
        self.assertEqual(play.duration, self.duration)
        self.assertEqual(repr(play),
                         "Play(Waveform(array([1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]), name='test'),"
                         " DriveChannel(1), name='test')")
        def convert_named_drive(self, instruction):
            """Return converted `Play`.

            Args:
                instruction (PulseQobjInstruction): pulse qobj
            Returns:
                Schedule: Converted and scheduled pulse
            """
            t0 = instruction.t0
            channel = self.get_channel(instruction.ch)
            return instructions.Play(pulse, channel) << t0
Esempio n. 5
0
    def test_play_sample_pulse(self):
        """Test play instruction with sample pulse."""
        d0 = pulse.DriveChannel(0)
        test_pulse = library.Waveform([0.0, 0.0])

        with pulse.build() as schedule:
            pulse.play(test_pulse, d0)

        reference = pulse.Schedule()
        reference += instructions.Play(test_pulse, d0)

        self.assertEqual(schedule, reference)
Esempio n. 6
0
    def test_play_parametric_pulse(self):
        """Test play instruction with parametric pulse."""
        d0 = pulse.DriveChannel(0)
        test_pulse = library.Constant(10, 1.0)

        with pulse.build() as schedule:
            pulse.play(test_pulse, d0)

        reference = pulse.Schedule()
        reference += instructions.Play(test_pulse, d0)

        self.assertEqual(schedule, reference)
    def convert_parametric(self, instruction):
        """Return the ParametricPulse implementation that is described by the instruction.

        Args:
            instruction (PulseQobjInstruction): pulse qobj
        Returns:
            Schedule: Schedule containing the converted pulse
        """
        t0 = instruction.t0
        channel = self.get_channel(instruction.ch)
        pulse = ParametricPulseShapes[instruction.pulse_shape].value(**instruction.parameters)
        return instructions.Play(pulse, channel) << t0
Esempio n. 8
0
    def test_play(self):
        """Test basic play instruction."""
        duration = 4
        pulse = library.Waveform([1.0] * duration, name='test')
        play = instructions.Play(pulse, channels.DriveChannel(1))

        self.assertIsInstance(play.id, int)
        self.assertEqual(play.name, pulse.name)
        self.assertEqual(play.duration, duration)
        self.assertEqual(repr(play),
                         "Play(Waveform(array([1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]), name='test'),"
                         " DriveChannel(1), name='test')")
Esempio n. 9
0
    def test_play_array_pulse(self):
        """Test play instruction on an array directly."""
        d0 = pulse.DriveChannel(0)
        test_array = np.array([0., 0.], dtype=np.complex_)

        with pulse.build() as schedule:
            pulse.play(test_array, d0)

        reference = pulse.Schedule()
        test_pulse = pulse.Waveform(test_array)
        reference += instructions.Play(test_pulse, d0)

        self.assertEqual(schedule, reference)
def _assemble_instructions(
    schedule: pulse.Schedule,
    instruction_converter: converters.InstructionToQobjConverter,
    run_config: RunConfig, user_pulselib: Dict[str, List[complex]]
) -> Tuple[List[qobj.PulseQobjInstruction], int]:
    """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns
    related metadata that will be assembled into the Qobj configuration. Lookup table for
    pulses defined in all experiments are registered in ``user_pulselib``. This object should be
    mutable python dictionary so that items are properly updated after each instruction assemble.
    The dictionary is not returned to avoid redundancy.

    Args:
        schedule: Schedule to assemble.
        instruction_converter: A converter instance which can convert PulseInstructions to
                               PulseQobjInstructions.
        run_config: Configuration of the runtime environment.
        user_pulselib: User pulse library from previous schedule.

    Returns:
        A list of converted instructions, the user pulse library dictionary (from pulse name to
        pulse samples), and the maximum number of readout memory slots used by this Schedule.
    """
    max_memory_slot = 0
    qobj_instructions = []

    acquire_instruction_map = defaultdict(list)
    for time, instruction in schedule.instructions:

        if (isinstance(instruction, instructions.Play)
                and isinstance(instruction.pulse, library.ParametricPulse)):
            pulse_shape = ParametricPulseShapes(type(instruction.pulse)).name
            if pulse_shape not in run_config.parametric_pulses:
                instruction = instructions.Play(
                    instruction.pulse.get_waveform(),
                    instruction.channel,
                    name=instruction.name)

        if (isinstance(instruction, instructions.Play)
                and isinstance(instruction.pulse, library.Waveform)):
            name = hashlib.sha256(instruction.pulse.samples).hexdigest()
            instruction = instructions.Play(library.Waveform(
                name=name, samples=instruction.pulse.samples),
                                            channel=instruction.channel,
                                            name=name)
            user_pulselib[name] = instruction.pulse.samples

        if isinstance(instruction, instructions.Acquire):
            if instruction.mem_slot:
                max_memory_slot = max(max_memory_slot,
                                      instruction.mem_slot.index)
            # Acquires have a single AcquireChannel per inst, but we have to bundle them
            # together into the Qobj as one instruction with many channels
            acquire_instruction_map[(time,
                                     instruction.duration)].append(instruction)
            continue

        if isinstance(instruction,
                      (instructions.Delay, instructions.Directive)):
            # delay instructions are ignored as timing is explicit within qobj
            continue

        qobj_instructions.append(instruction_converter(time, instruction))

    if acquire_instruction_map:
        if hasattr(run_config, 'meas_map'):
            _validate_meas_map(acquire_instruction_map, run_config.meas_map)
        for (time, _), instrs in acquire_instruction_map.items():
            qobj_instructions.append(
                instruction_converter.convert_bundled_acquires(time, instrs), )

    return qobj_instructions, max_memory_slot
 def test_play_non_pulse_ch_raises(self):
     """Test that play instruction on non-pulse channel raises a pulse error."""
     with self.assertRaises(exceptions.PulseError):
         instructions.Play(self.pulse_op, channels.AcquireChannel(0))
def _assemble_instructions(
    sched: Union[pulse.Schedule, pulse.ScheduleBlock],
    instruction_converter: converters.InstructionToQobjConverter,
    run_config: RunConfig,
    user_pulselib: Dict[str, List[complex]],
) -> Tuple[List[qobj.PulseQobjInstruction], int]:
    """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns
    related metadata that will be assembled into the Qobj configuration. Lookup table for
    pulses defined in all experiments are registered in ``user_pulselib``. This object should be
    mutable python dictionary so that items are properly updated after each instruction assemble.
    The dictionary is not returned to avoid redundancy.

    Args:
        sched: Schedule to assemble.
        instruction_converter: A converter instance which can convert PulseInstructions to
                               PulseQobjInstructions.
        run_config: Configuration of the runtime environment.
        user_pulselib: User pulse library from previous schedule.

    Returns:
        A list of converted instructions, the user pulse library dictionary (from pulse name to
        pulse samples), and the maximum number of readout memory slots used by this Schedule.
    """
    sched = transforms.target_qobj_transform(sched)

    max_memory_slot = 0
    qobj_instructions = []

    acquire_instruction_map = defaultdict(list)
    for time, instruction in sched.instructions:

        if isinstance(instruction, instructions.Play):
            if isinstance(instruction.pulse, (library.ParametricPulse, library.SymbolicPulse)):
                is_backend_supported = True
                try:
                    pulse_shape = ParametricPulseShapes(type(instruction.pulse)).name
                    if pulse_shape not in run_config.parametric_pulses:
                        is_backend_supported = False
                except ValueError:
                    # Custom pulse class, or bare SymbolicPulse object.
                    is_backend_supported = False

                if not is_backend_supported:
                    instruction = instructions.Play(
                        instruction.pulse.get_waveform(), instruction.channel, name=instruction.name
                    )

            if isinstance(instruction.pulse, library.Waveform):
                name = hashlib.sha256(instruction.pulse.samples).hexdigest()
                instruction = instructions.Play(
                    library.Waveform(name=name, samples=instruction.pulse.samples),
                    channel=instruction.channel,
                    name=name,
                )
                user_pulselib[name] = instruction.pulse.samples

        # ignore explicit delay instrs on acq channels as they are invalid on IBMQ backends;
        # timing of other instrs will still be shifted appropriately
        if isinstance(instruction, instructions.Delay) and isinstance(
            instruction.channel, channels.AcquireChannel
        ):
            continue

        if isinstance(instruction, instructions.Acquire):
            if instruction.mem_slot:
                max_memory_slot = max(max_memory_slot, instruction.mem_slot.index)
            # Acquires have a single AcquireChannel per inst, but we have to bundle them
            # together into the Qobj as one instruction with many channels
            acquire_instruction_map[(time, instruction.duration)].append(instruction)
            continue

        qobj_instructions.append(instruction_converter(time, instruction))

    if acquire_instruction_map:
        if hasattr(run_config, "meas_map"):
            _validate_meas_map(acquire_instruction_map, run_config.meas_map)
        for (time, _), instrs in acquire_instruction_map.items():
            qobj_instructions.append(
                instruction_converter.convert_bundled_acquires(time, instrs),
            )

    return qobj_instructions, max_memory_slot