Repository: Qiskit/qiskit

Issue:
execute function does not recognize ScheduleBlock
<!-- ⚠️ 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**: main
- **Python version**: N/A
- **Operating system**: N/A

### What is the current behavior?
When `pulse.build` produces a [ScheduleBlock](https://qiskit.org/documentation/stubs/qiskit.pulse.ScheduleBlock.html) and it is submitted to be executed by the backend via `execute(schedule_block, backend)` an error `AttributeError: 'ScheduleBlock' object has no attribute 'calibrations' .` is raised.


### Steps to reproduce the problem

```python
# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, transpile, Aer, IBMQ, pulse, execute

provider = IBMQ.load_account()
backend = provider.get_backend("ibmq_armonk")

with pulse.build() as sched_block:
    pulse.play(pulse.Constant(160, 1.0), pulse.DriveChannel(0))

execute(sched_block, backend)
```


### What is the expected behavior?
Execute should accept a `ScheduleBlock` as well as `execute` and this should be submitted to `backend.run`.


### Suggested solutions
Within `execute` when matching on `Schedule` instead match on a `Union[Schedule, ScheduleBlock]`.




Discussion:
@taalexander can I have a go at this?
If yes, would you be able to elaborate what you mean when you say match on `Union[Schedule, ScheduleBlock]`? Sorry but I was a bit new to `Pulse`. Thanks!

go for it @TheGupta2012, I've assigned to you 😄 You can check out `qiskit/execute_function.py` for how Schedules are handled, and adapt to handle ScheduleBlocks as well

Resolving patch:
diff --git a/qiskit/execute_function.py b/qiskit/execute_function.py
--- a/qiskit/execute_function.py
+++ b/qiskit/execute_function.py
@@ -25,7 +25,7 @@
 from qiskit.providers import BaseBackend
 from qiskit.providers.backend import Backend
 from qiskit.qobj.utils import MeasLevel, MeasReturnType
-from qiskit.pulse import Schedule
+from qiskit.pulse import Schedule, ScheduleBlock
 from qiskit.exceptions import QiskitError
 
 logger = logging.getLogger(__name__)
@@ -272,8 +272,8 @@ def execute(
 
             job = execute(qc, backend, shots=4321)
     """
-    if isinstance(experiments, Schedule) or (
-        isinstance(experiments, list) and isinstance(experiments[0], Schedule)
+    if isinstance(experiments, (Schedule, ScheduleBlock)) or (
+        isinstance(experiments, list) and isinstance(experiments[0], (Schedule, ScheduleBlock))
     ):
         # do not transpile a schedule circuit
         if schedule_circuit:


Test patch:
diff --git a/qiskit/test/mock/fake_backend.py b/qiskit/test/mock/fake_backend.py
--- a/qiskit/test/mock/fake_backend.py
+++ b/qiskit/test/mock/fake_backend.py
@@ -119,13 +119,13 @@ def run(self, run_input, **kwargs):
         """Main job in simulator"""
         circuits = run_input
         pulse_job = None
-        if isinstance(circuits, pulse.Schedule):
+        if isinstance(circuits, (pulse.Schedule, pulse.ScheduleBlock)):
             pulse_job = True
         elif isinstance(circuits, circuit.QuantumCircuit):
             pulse_job = False
         elif isinstance(circuits, list):
             if circuits:
-                if all(isinstance(x, pulse.Schedule) for x in circuits):
+                if all(isinstance(x, (pulse.Schedule, pulse.ScheduleBlock)) for x in circuits):
                     pulse_job = True
                 elif all(isinstance(x, circuit.QuantumCircuit) for x in circuits):
                     pulse_job = False
diff --git a/test/python/pulse/test_block.py b/test/python/pulse/test_block.py
--- a/test/python/pulse/test_block.py
+++ b/test/python/pulse/test_block.py
@@ -13,12 +13,13 @@
 # pylint: disable=invalid-name
 
 """Test cases for the pulse schedule block."""
-
+import unittest
 from qiskit import pulse, circuit
 from qiskit.pulse import transforms
 from qiskit.pulse.exceptions import PulseError
 from qiskit.test import QiskitTestCase
-from qiskit.test.mock import FakeOpenPulse2Q
+from qiskit.test.mock import FakeOpenPulse2Q, FakeArmonk
+from qiskit.utils import has_aer
 
 
 class BaseTestBlock(QiskitTestCase):
@@ -429,6 +430,18 @@ def test_inherit_from(self):
         self.assertEqual(new_sched.name, ref_name)
         self.assertDictEqual(new_sched.metadata, ref_metadata)
 
+    @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.")
+    def test_execute_block(self):
+        """Test executing a ScheduleBlock on a Pulse backend"""
+
+        with pulse.build(name="test_block") as sched_block:
+            pulse.play(pulse.Constant(160, 1.0), pulse.DriveChannel(0))
+            pulse.acquire(50, pulse.MeasureChannel(0), pulse.MemorySlot(0))
+
+        backend = FakeArmonk()
+        test_result = backend.run(sched_block).result()
+        self.assertDictEqual(test_result.get_counts(), {"0": 1024})
+
 
 class TestBlockEquality(BaseTestBlock):
     """Test equality of blocks.