def test_parametric_pulses_with_no_duplicates(self):
        """Test parametric pulses with no duplicates."""
        schedule = Schedule()
        drive_channel = DriveChannel(0)
        schedule += Play(Gaussian(duration=25, sigma=4, amp=0.5j),
                         drive_channel)
        schedule += Play(Gaussian(duration=25, sigma=4, amp=0.49j),
                         drive_channel)
        schedule += Play(
            GaussianSquare(duration=150, amp=0.2, sigma=8, width=140),
            drive_channel)
        schedule += Play(
            GaussianSquare(duration=150, amp=0.19, sigma=8, width=140),
            drive_channel)
        schedule += Play(Constant(duration=150, amp=0.1 + 0.4j), drive_channel)
        schedule += Play(Constant(duration=150, amp=0.1 + 0.41j),
                         drive_channel)
        schedule += Play(Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4),
                         drive_channel)
        schedule += Play(Drag(duration=25, amp=0.2 + 0.31j, sigma=7.8, beta=4),
                         drive_channel)

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)
        self.assertEqual(len(original_pulse_ids), len(compressed_pulse_ids))
    def test_with_different_channels(self):
        """Test with different channels."""
        schedule = Schedule()
        schedule += Play(Waveform([0.0, 0.1]), DriveChannel(0))
        schedule += Play(Waveform([0.0, 0.1]), DriveChannel(1))

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)
        self.assertEqual(len(original_pulse_ids), 2)
        self.assertEqual(len(compressed_pulse_ids), 1)
    def test_sample_pulses_with_tolerance(self):
        """Test sample pulses with tolerance."""
        schedule = Schedule()
        schedule += Play(Waveform([0.0, 0.1001], epsilon=1e-3), DriveChannel(0))
        schedule += Play(Waveform([0.0, 0.1], epsilon=1e-3), DriveChannel(1))

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)
        self.assertEqual(len(original_pulse_ids), 2)
        self.assertEqual(len(compressed_pulse_ids), 1)
    def test_no_duplicates(self):
        """Test with no pulse duplicates."""
        schedule = Schedule()
        drive_channel = DriveChannel(0)
        schedule += Play(Waveform([0.0, 1.0]), drive_channel)
        schedule += Play(Waveform([0.0, 0.9]), drive_channel)
        schedule += Play(Waveform([0.0, 0.3]), drive_channel)

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)
        self.assertEqual(len(original_pulse_ids), len(compressed_pulse_ids))
    def test_with_duplicates(self):
        """Test compression of schedule."""
        schedule = Schedule()
        drive_channel = DriveChannel(0)
        schedule += Play(Waveform([0.0, 0.1]), drive_channel)
        schedule += Play(Waveform([0.0, 0.1]), drive_channel)

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)

        self.assertEqual(len(compressed_pulse_ids), 1)
        self.assertEqual(len(original_pulse_ids), 2)
        self.assertTrue(next(iter(compressed_pulse_ids)) in original_pulse_ids)
    def test_sample_pulse_with_clipping(self):
        """Test sample pulses with clipping."""
        schedule = Schedule()
        drive_channel = DriveChannel(0)
        schedule += Play(Waveform([0.0, 1.0]), drive_channel)
        schedule += Play(Waveform([0.0, 1.001], epsilon=1e-3), drive_channel)
        schedule += Play(Waveform([0.0, 1.0000000001]), drive_channel)

        compressed_schedule = transforms.compress_pulses([schedule])
        original_pulse_ids = get_pulse_ids([schedule])
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)

        self.assertEqual(len(compressed_pulse_ids), 1)
        self.assertEqual(len(original_pulse_ids), 3)
        self.assertTrue(next(iter(compressed_pulse_ids)) in original_pulse_ids)
    def test_multiple_schedules(self):
        """Test multiple schedules."""
        schedules = []
        for _ in range(2):
            schedule = Schedule()
            drive_channel = DriveChannel(0)
            schedule += Play(Waveform([0.0, 0.1]), drive_channel)
            schedule += Play(Waveform([0.0, 0.1]), drive_channel)
            schedule += Play(Waveform([0.0, 0.2]), drive_channel)
            schedules.append(schedule)

        compressed_schedule = transforms.compress_pulses(schedules)
        original_pulse_ids = get_pulse_ids(schedules)
        compressed_pulse_ids = get_pulse_ids(compressed_schedule)
        self.assertEqual(len(original_pulse_ids), 6)
        self.assertEqual(len(compressed_pulse_ids), 2)
def _assemble_experiments(
    schedules: List[Union[pulse.ScheduleComponent,
                          Tuple[int, pulse.ScheduleComponent]]],
    lo_converter: converters.LoConfigConverter, run_config: RunConfig
) -> Tuple[List[qobj.PulseQobjExperiment], Dict[str, Any]]:
    """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that
    will be assembled into the Qobj configuration.

    Args:
        schedules: Schedules to assemble.
        lo_converter: The configured frequency converter and validator.
        run_config: Configuration of the runtime environment.

    Returns:
        The list of assembled experiments, and the dictionary of related experiment config.

    Raises:
        QiskitError: when frequency settings are not compatible with the experiments.
    """
    freq_configs = [
        lo_converter(lo_dict)
        for lo_dict in getattr(run_config, 'schedule_los', [])
    ]

    if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]:
        raise QiskitError(
            'Invalid frequency setting is specified. If the frequency is specified, '
            'it should be configured the same for all schedules, configured for each '
            'schedule, or a list of frequencies should be provided for a single '
            'frequency sweep schedule.')

    instruction_converter = getattr(run_config, 'instruction_converter',
                                    converters.InstructionToQobjConverter)
    instruction_converter = instruction_converter(qobj.PulseQobjInstruction,
                                                  **run_config.to_dict())

    schedules = [
        sched if isinstance(sched, pulse.Schedule) else pulse.Schedule(sched)
        for sched in schedules
    ]
    compressed_schedules = transforms.compress_pulses(schedules)

    user_pulselib = {}
    experiments = []
    for idx, schedule in enumerate(compressed_schedules):
        qobj_instructions, max_memory_slot = _assemble_instructions(
            schedule, instruction_converter, run_config, user_pulselib)

        # TODO: add other experimental header items (see circuit assembler)
        qobj_experiment_header = qobj.QobjExperimentHeader(
            memory_slots=max_memory_slot + 1,  # Memory slots are 0 indexed
            name=schedule.name or 'Experiment-%d' % idx)

        experiment = qobj.PulseQobjExperiment(header=qobj_experiment_header,
                                              instructions=qobj_instructions)
        if freq_configs:
            # This handles the cases where one frequency setting applies to all experiments and
            # where each experiment has a different frequency
            freq_idx = idx if len(freq_configs) != 1 else 0
            experiment.config = freq_configs[freq_idx]

        experiments.append(experiment)

    # Frequency sweep
    if freq_configs and len(experiments) == 1:
        experiment = experiments[0]
        experiments = []
        for freq_config in freq_configs:
            experiments.append(
                qobj.PulseQobjExperiment(header=experiment.header,
                                         instructions=experiment.instructions,
                                         config=freq_config))

    # Top level Qobj configuration
    experiment_config = {
        'pulse_library': [
            qobj.PulseLibraryItem(name=name, samples=samples)
            for name, samples in user_pulselib.items()
        ],
        'memory_slots':
        max([exp.header.memory_slots for exp in experiments])
    }

    return experiments, experiment_config
def _assemble_experiments(
    schedules: List[Union["schedule.ScheduleComponent",
                          Tuple[int, "schedule.ScheduleComponent"]]],
    lo_converter: converters.LoConfigConverter,
    run_config: RunConfig,
) -> Tuple[List[qobj.PulseQobjExperiment], Dict[str, Any]]:
    """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that
    will be assembled into the Qobj configuration.

    Args:
        schedules: Schedules to assemble.
        lo_converter: The configured frequency converter and validator.
        run_config: Configuration of the runtime environment.

    Returns:
        The list of assembled experiments, and the dictionary of related experiment config.

    Raises:
        QiskitError: when frequency settings are not compatible with the experiments.
    """
    freq_configs = [
        lo_converter(lo_dict)
        for lo_dict in getattr(run_config, "schedule_los", [])
    ]

    if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]:
        raise QiskitError(
            "Invalid 'schedule_los' setting specified. If specified, it should be "
            "either have a single entry to apply the same LOs for each schedule or "
            "have length equal to the number of schedules.")

    instruction_converter = getattr(run_config, "instruction_converter",
                                    converters.InstructionToQobjConverter)
    instruction_converter = instruction_converter(qobj.PulseQobjInstruction,
                                                  **run_config.to_dict())

    formatted_schedules = [
        transforms.target_qobj_transform(sched) for sched in schedules
    ]
    compressed_schedules = transforms.compress_pulses(formatted_schedules)

    user_pulselib = {}
    experiments = []
    for idx, sched in enumerate(compressed_schedules):
        qobj_instructions, max_memory_slot = _assemble_instructions(
            sched, instruction_converter, run_config, user_pulselib)

        metadata = sched.metadata
        if metadata is None:
            metadata = {}
        # TODO: add other experimental header items (see circuit assembler)
        qobj_experiment_header = qobj.QobjExperimentHeader(
            memory_slots=max_memory_slot + 1,  # Memory slots are 0 indexed
            name=sched.name or "Experiment-%d" % idx,
            metadata=metadata,
        )

        experiment = qobj.PulseQobjExperiment(header=qobj_experiment_header,
                                              instructions=qobj_instructions)
        if freq_configs:
            # This handles the cases where one frequency setting applies to all experiments and
            # where each experiment has a different frequency
            freq_idx = idx if len(freq_configs) != 1 else 0
            experiment.config = freq_configs[freq_idx]

        experiments.append(experiment)

    # Frequency sweep
    if freq_configs and len(experiments) == 1:
        experiment = experiments[0]
        experiments = []
        for freq_config in freq_configs:
            experiments.append(
                qobj.PulseQobjExperiment(
                    header=experiment.header,
                    instructions=experiment.instructions,
                    config=freq_config,
                ))

    # Top level Qobj configuration
    experiment_config = {
        "pulse_library": [
            qobj.PulseLibraryItem(name=name, samples=samples)
            for name, samples in user_pulselib.items()
        ],
        "memory_slots":
        max(exp.header.memory_slots for exp in experiments),
    }

    return experiments, experiment_config