Repository: Qiskit/qiskit

Issue:
Schedule drawing fails if schedule is empty
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master
- **Python version**: 3.7
- **Operating system**: OsX

### What is the current behavior?
Schedule drawing fails if schedule is empty


### Steps to reproduce the problem
```python
from qiskit import pulse
fc_pi = pulse.FrameChange(np.pi)
dc0 = pulse.DriveChannel(0)
fc_pi_dc0 = fc_pi.to_instruction(dc0)

sched = pulse.Schedule()
sched += fc_pi_dc0
sched.draw()
```

### What is the expected behavior?



### Suggested solutions





Discussion:
Thanks for reporting the issue. I noticed this issue and have two options;
- raise an error when a schedule is empty
- modify schedule duration to have at least 1

I can't find useful example to visualize an empty schedule, and I think it won't be a problem to do the first one. but It's still worth to take second option?
We should replicate the null behaviour of the `QuantumCircuit.draw` which is to output a null canvas. However, please note in the example I gave above the Schedule is not empty, and is failing.

Resolving patch:
diff --git a/qiskit/visualization/pulse/matplotlib.py b/qiskit/visualization/pulse/matplotlib.py
--- a/qiskit/visualization/pulse/matplotlib.py
+++ b/qiskit/visualization/pulse/matplotlib.py
@@ -356,6 +356,12 @@ def _count_valid_waveforms(self, channels, scaling=1, channels_to_plot=None,
                                 max(np.abs(np.imag(waveform))))
                     n_valid_waveform += 1
                     events.enable = True
+
+        # when input schedule is empty or comprises only frame changes,
+        # we need to overwrite maximum amplitude by a value greater than zero,
+        # otherwise auto axis scaling will fail with zero division.
+        v_max = v_max or 1
+
         if scaling:
             v_max = 0.5 * scaling
         else:
@@ -489,7 +495,13 @@ def _draw_channels(self, ax, output_channels, interp_method, t0, tf, dt, v_max,
                 # plot waveform
                 waveform = events.waveform
                 time = np.arange(t0, tf + 1, dtype=float) * dt
-                time, re, im = interp_method(time, waveform, self.style.num_points)
+                if waveform.any():
+                    time, re, im = interp_method(time, waveform, self.style.num_points)
+                else:
+                    # when input schedule is empty or comprises only frame changes,
+                    # we should avoid interpolation due to lack of data points.
+                    # instead, it just returns vector of zero.
+                    re, im = np.zeros_like(time), np.zeros_like(time)
                 color = self._get_channel_color(channel)
                 # scaling and offset
                 re = v_max * re + y0
@@ -560,7 +572,10 @@ def draw(self, schedule, dt, interp_method, plot_range,
             tf = int(np.floor(plot_range[1]/dt))
         else:
             t0 = 0
-            tf = schedule.stop_time
+            # when input schedule is empty or comprises only frame changes,
+            # we need to overwrite pulse duration by an integer greater than zero,
+            # otherwise waveform returns empty array and matplotlib will be crashed.
+            tf = schedule.stop_time or 1
         # prepare waveform channels
         (channels, output_channels,
          snapshot_channels) = self._build_channels(schedule, channels_to_plot, t0, tf)


Test patch: