# Buggy code
def align_measures(
    schedules: Iterable[interfaces.ScheduleComponent],
    inst_map: Optional[InstructionScheduleMap] = None,
    cal_gate: str = "u3",
    max_calibration_duration: Optional[int] = None,
    align_time: Optional[int] = None,
) -> List[Schedule]:
    """Return new schedules where measurements occur at the same physical time. Minimum measurement
    wait time (to allow for calibration pulses) is enforced.

    This is only defined for schedules that are acquire-less or acquire-final per channel: a
    schedule with pulses or acquires occurring on a channel which has already had a measurement will
    throw an error.

    Args:
        schedules: Collection of schedules to be aligned together
        inst_map: Mapping of circuit operations to pulse schedules
        cal_gate: The name of the gate to inspect for the calibration time
        max_calibration_duration: If provided, inst_map and cal_gate will be ignored
        align_time: If provided, this will be used as final align time.

    Returns:
        The input list of schedules transformed to have their measurements aligned.

    Raises:
        PulseError: if an acquire or pulse is encountered on a channel that has already been part
                    of an acquire, or if align_time is negative
    """

    def calculate_align_time():
        """Return the the max between the duration of the calibration time and the absolute time
        of the latest scheduled acquire.
        """
        nonlocal max_calibration_duration
        if max_calibration_duration is None:
            max_calibration_duration = get_max_calibration_duration()
        align_time = max_calibration_duration
        for schedule in schedules:
            last_acquire = 0
            acquire_times = [
                time
                for time, inst in schedule.instructions
                if isinstance(inst, instructions.Acquire)
            ]
            if acquire_times:
                last_acquire = max(acquire_times)
            align_time = max(align_time, last_acquire)
        return align_time

    def get_max_calibration_duration():
        """Return the time needed to allow for readout discrimination calibration pulses."""
        max_calibration_duration = 0
        for qubits in inst_map.qubits_with_instruction(cal_gate):
            cmd = inst_map.get(cal_gate, qubits, np.pi, 0, np.pi)
            max_calibration_duration = max(cmd.duration, max_calibration_duration)
        return max_calibration_duration

    if align_time is None and max_calibration_duration is None and inst_map is None:
        raise exceptions.PulseError(
            "Must provide a inst_map, an alignment time, or a calibration duration."
        )
    if align_time is not None and align_time < 0:
        raise exceptions.PulseError("Align time cannot be negative.")
    if align_time is None:
        align_time = calculate_align_time()

    # Shift acquires according to the new scheduled time
    new_schedules = []
    for schedule in schedules:
        new_schedule = Schedule(name=schedule.name)
        acquired_channels = set()
        measured_channels = set()

        for time, inst in schedule.instructions:
            for chan in inst.channels:
                if isinstance(chan, chans.MeasureChannel):
                    if chan.index in measured_channels:
                        raise exceptions.PulseError(
                            "Multiple measurements are "
                            "not supported by this "
                            "rescheduling pass."
                        )
                elif chan.index in acquired_channels:
                    raise exceptions.PulseError(
                        "Pulse encountered on channel "
                        "{0} after acquire on "
                        "same channel.".format(chan.index)
                    )

            if isinstance(inst, instructions.Acquire):
                if time > align_time:
                    warnings.warn(
                        "You provided an align_time which is scheduling an acquire "
                        "sooner than it was scheduled for in the original Schedule."
                    )
                new_schedule |= inst << align_time
                acquired_channels.add(inst.channel.index)
            elif isinstance(inst.channels[0], chans.MeasureChannel):
                new_schedule |= inst << align_time
                measured_channels.update({a.index for a in inst.channels})
            else:
                new_schedule |= inst << time

        new_schedules.append(new_schedule)

    return new_schedules


# Traceback (RecursionError)
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-99-d345b17804b7> in <module>
11
12 transformed_sched = transforms.compress_pulses([rb_scheds])
---> 13 transformed_sched[0].instructions

~/qiskit/qiskit-terra/qiskit/pulse/schedule.py in instructions(self)
126                     sorted(chan.name for chan in inst.channels))
127
--> 128         return tuple(sorted(self._instructions(), key=key))
129
130     def ch_duration(self, *channels: List[Channel]) -> int:

~/qiskit/qiskit-terra/qiskit/pulse/schedule.py in _instructions(self, time)
174         """
175         for insert_time, child_sched in self._children:
--> 176             yield from child_sched._instructions(time + insert_time)
177
178     def union(self,

... last 1 frames repeated, from the frame below ...

~/qiskit/qiskit-terra/qiskit/pulse/schedule.py in _instructions(self, time)
174         """
175         for insert_time, child_sched in self._children:
--> 176             yield from child_sched._instructions(time + insert_time)
177
178     def union(self,

RecursionError: maximum recursion depth exceeded while calling a Python object

# Fixed code
def align_measures(
    schedules: Iterable[interfaces.ScheduleComponent],
    inst_map: Optional[InstructionScheduleMap] = None,
    cal_gate: str = "u3",
    max_calibration_duration: Optional[int] = None,
    align_time: Optional[int] = None,
) -> List[Schedule]:
    """Return new schedules where measurements occur at the same physical time. Minimum measurement
    wait time (to allow for calibration pulses) is enforced.

    This is only defined for schedules that are acquire-less or acquire-final per channel: a
    schedule with pulses or acquires occurring on a channel which has already had a measurement will
    throw an error.

    Args:
        schedules: Collection of schedules to be aligned together
        inst_map: Mapping of circuit operations to pulse schedules
        cal_gate: The name of the gate to inspect for the calibration time
        max_calibration_duration: If provided, inst_map and cal_gate will be ignored
        align_time: If provided, this will be used as final align time.

    Returns:
        The input list of schedules transformed to have their measurements aligned.

    Raises:
        PulseError: if an acquire or pulse is encountered on a channel that has already been part
                    of an acquire, or if align_time is negative
    """

    def calculate_align_time():
        """Return the the max between the duration of the calibration time and the absolute time
        of the latest scheduled acquire.
        """
        nonlocal max_calibration_duration
        if max_calibration_duration is None:
            max_calibration_duration = get_max_calibration_duration()
        align_time = max_calibration_duration
        for schedule in schedules:
            last_acquire = 0
            acquire_times = [
                time
                for time, inst in schedule.instructions
                if isinstance(inst, instructions.Acquire)
            ]
            if acquire_times:
                last_acquire = max(acquire_times)
            align_time = max(align_time, last_acquire)
        return align_time

    def get_max_calibration_duration():
        """Return the time needed to allow for readout discrimination calibration pulses."""
        max_calibration_duration = 0
        for qubits in inst_map.qubits_with_instruction(cal_gate):
            cmd = inst_map.get(cal_gate, qubits, np.pi, 0, np.pi)
            max_calibration_duration = max(cmd.duration, max_calibration_duration)
        return max_calibration_duration

    if align_time is None and max_calibration_duration is None and inst_map is None:
        raise exceptions.PulseError(
            "Must provide a inst_map, an alignment time, or a calibration duration."
        )
    if align_time is not None and align_time < 0:
        raise exceptions.PulseError("Align time cannot be negative.")
    if align_time is None:
        align_time = calculate_align_time()

    # Shift acquires according to the new scheduled time
    new_schedules = []
    for schedule in schedules:
        new_schedule = Schedule(name=schedule.name)
        acquired_channels = set()
        measured_channels = set()

        for time, inst in schedule.instructions:
            for chan in inst.channels:
                if isinstance(chan, chans.MeasureChannel):
                    if chan.index in measured_channels:
                        raise exceptions.PulseError(
                            "Multiple measurements are "
                            "not supported by this "
                            "rescheduling pass."
                        )
                elif chan.index in acquired_channels:
                    raise exceptions.PulseError(
                        "Pulse encountered on channel "
                        "{0} after acquire on "
                        "same channel.".format(chan.index)
                    )

            if isinstance(inst, instructions.Acquire):
                if time > align_time:
                    warnings.warn(
                        "You provided an align_time which is scheduling an acquire "
                        "sooner than it was scheduled for in the original Schedule."
                    )
                new_schedule.insert(align_time, inst, inplace=True)
                acquired_channels.add(inst.channel.index)
            elif isinstance(inst.channels[0], chans.MeasureChannel):
                new_schedule.insert(align_time, inst, inplace=True)
                measured_channels.update({a.index for a in inst.channels})
            else:
                new_schedule.insert(time, inst, inplace=True)

        new_schedules.append(new_schedule)

    return new_schedules