Repository: Qiskit/qiskit

Issue:
Enum input for meas_return to assembler is being converted incorrectly 
<!-- ⚠️ 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**:
- **Python version**:
- **Operating system**:

### What is the current behavior?
Enum input to the assembler is being converted incorrectly to a string.


### Steps to reproduce the problem
```python
qobj = assemble(pulse.Schedule(), backend)
backend.run(qobj)
```

### What is the expected behavior?



### Suggested solutions
Convert enum to a string preemptively. 




Discussion:

Resolving patch:
diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py
--- a/qiskit/assembler/assemble_schedules.py
+++ b/qiskit/assembler/assemble_schedules.py
@@ -20,6 +20,7 @@
                          PulseQobjInstruction, PulseQobjExperimentConfig,
                          PulseQobjExperiment, PulseQobjConfig, PulseLibraryItem)
 from qiskit.qobj.converters import InstructionToQobjConverter, LoConfigConverter
+from qiskit.qobj.utils import MeasLevel, MeasReturnType
 
 
 def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
@@ -54,6 +55,15 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
     meas_lo_range = qobj_config.pop('meas_lo_range', None)
     meas_map = qobj_config.pop('meas_map', None)
 
+    # convert enums to serialized values
+    meas_return = qobj_config.get('meas_return', 'avg')
+    if isinstance(meas_return, MeasReturnType):
+        qobj_config['meas_return'] = meas_return.value
+
+    meas_level = qobj_config.get('meas_return', 2)
+    if isinstance(meas_level, MeasLevel):
+        qobj_config['meas_level'] = meas_level.value
+
     instruction_converter = instruction_converter(PulseQobjInstruction, **qobj_config)
 
     lo_converter = LoConfigConverter(PulseQobjExperimentConfig,


Test patch:
diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py
--- a/test/python/compiler/test_assembler.py
+++ b/test/python/compiler/test_assembler.py
@@ -24,7 +24,8 @@
 from qiskit.compiler.assemble import assemble
 from qiskit.exceptions import QiskitError
 from qiskit.pulse.channels import MemorySlot, AcquireChannel
-from qiskit.qobj import QasmQobj
+from qiskit.qobj import QasmQobj, validate_qobj_against_schema
+from qiskit.qobj.utils import MeasLevel, MeasReturnType
 from qiskit.test import QiskitTestCase
 from qiskit.test.mock import FakeOpenPulse2Q
 
@@ -43,6 +44,8 @@ def test_assemble_single_circuit(self):
         circ.measure(qr, cr)
 
         qobj = assemble(circ, shots=2000, memory=True)
+        validate_qobj_against_schema(qobj)
+
         self.assertIsInstance(qobj, QasmQobj)
         self.assertEqual(qobj.config.shots, 2000)
         self.assertEqual(qobj.config.memory, True)
@@ -68,6 +71,8 @@ def test_assemble_multiple_circuits(self):
         circ1.measure(qr1, qc1)
 
         qobj = assemble([circ0, circ1], shots=100, memory=False, seed_simulator=6)
+        validate_qobj_against_schema(qobj)
+
         self.assertIsInstance(qobj, QasmQobj)
         self.assertEqual(qobj.config.seed_simulator, 6)
         self.assertEqual(len(qobj.experiments), 2)
@@ -86,6 +91,8 @@ def test_assemble_no_run_config(self):
         circ.measure(qr, qc)
 
         qobj = assemble(circ)
+        validate_qobj_against_schema(qobj)
+
         self.assertIsInstance(qobj, QasmQobj)
         self.assertEqual(qobj.config.shots, 1024)
 
@@ -97,6 +104,8 @@ def test_assemble_initialize(self):
         circ.initialize([1/np.sqrt(2), 0, 0, 1/np.sqrt(2)], q[:])
 
         qobj = assemble(circ)
+        validate_qobj_against_schema(qobj)
+
         self.assertIsInstance(qobj, QasmQobj)
         self.assertEqual(qobj.experiments[0].instructions[0].name, 'initialize')
         np.testing.assert_almost_equal(qobj.experiments[0].instructions[0].params,
@@ -110,7 +119,10 @@ def test_assemble_opaque_inst(self):
         c = ClassicalRegister(4, name='c')
         circ = QuantumCircuit(q, c, name='circ')
         circ.append(opaque_inst, [q[0], q[2], q[5], q[3]], [c[3], c[0]])
+
         qobj = assemble(circ)
+        validate_qobj_against_schema(qobj)
+
         self.assertIsInstance(qobj, QasmQobj)
         self.assertEqual(len(qobj.experiments[0].instructions), 1)
         self.assertEqual(qobj.experiments[0].instructions[0].name, 'my_inst')
@@ -131,6 +143,7 @@ def test_measure_to_registers_when_conditionals(self):
         qc.h(qr[1]).c_if(cr2, 3)
 
         qobj = assemble(qc)
+        validate_qobj_against_schema(qobj)
 
         first_measure, second_measure = [op for op in qobj.experiments[0].instructions
                                          if op.name == 'measure']
@@ -149,6 +162,7 @@ def test_convert_to_bfunc_plus_conditional(self):
         qc.h(qr[0]).c_if(cr, 1)
 
         qobj = assemble(qc)
+        validate_qobj_against_schema(qobj)
 
         bfunc_op, h_op = qobj.experiments[0].instructions
 
@@ -172,6 +186,7 @@ def test_resize_value_to_register(self):
         qc.h(qr[0]).c_if(cr2, 2)
 
         qobj = assemble(qc)
+        validate_qobj_against_schema(qobj)
 
         bfunc_op, h_op = qobj.experiments[0].instructions
 
@@ -268,6 +283,7 @@ def test_assemble_circuits_binds_parameters(self):
                                          {x: 1, y: 1}]}
 
         qobj = assemble([qc1, qc2, qc3], **bind_args)
+        validate_qobj_against_schema(qobj)
 
         self.assertEqual(len(qobj.experiments), 9)
         self.assertEqual([len(expt.instructions) for expt in qobj.experiments],
@@ -338,8 +354,9 @@ def test_assemble_single_schedule_without_lo_config(self):
                         meas_lo_freq=self.default_meas_lo_freq,
                         schedule_los=[],
                         **self.config)
-        test_dict = qobj.to_dict()
+        validate_qobj_against_schema(qobj)
 
+        test_dict = qobj.to_dict()
         self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0])
         self.assertEqual(len(test_dict['experiments']), 1)
         self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2)
@@ -351,8 +368,9 @@ def test_assemble_multi_schedules_without_lo_config(self):
                         qubit_lo_freq=self.default_qubit_lo_freq,
                         meas_lo_freq=self.default_meas_lo_freq,
                         **self.config)
-        test_dict = qobj.to_dict()
+        validate_qobj_against_schema(qobj)
 
+        test_dict = qobj.to_dict()
         self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0])
         self.assertEqual(len(test_dict['experiments']), 2)
         self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2)
@@ -365,8 +383,9 @@ def test_assemble_single_schedule_with_lo_config(self):
                         meas_lo_freq=self.default_meas_lo_freq,
                         schedule_los=self.user_lo_config,
                         **self.config)
-        test_dict = qobj.to_dict()
+        validate_qobj_against_schema(qobj)
 
+        test_dict = qobj.to_dict()
         self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.91, 5.0])
         self.assertEqual(len(test_dict['experiments']), 1)
         self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2)
@@ -379,8 +398,9 @@ def test_assemble_single_schedule_with_lo_config_dict(self):
                         meas_lo_freq=self.default_meas_lo_freq,
                         schedule_los=self.user_lo_config_dict,
                         **self.config)
-        test_dict = qobj.to_dict()
+        validate_qobj_against_schema(qobj)
 
+        test_dict = qobj.to_dict()
         self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.91, 5.0])
         self.assertEqual(len(test_dict['experiments']), 1)
         self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2)
@@ -409,8 +429,9 @@ def test_assemble_multi_schedules_with_multi_lo_configs(self):
                         meas_lo_freq=self.default_meas_lo_freq,
                         schedule_los=[self.user_lo_config, self.user_lo_config],
                         **self.config)
-        test_dict = qobj.to_dict()
+        validate_qobj_against_schema(qobj)
 
+        test_dict = qobj.to_dict()
         self.assertListEqual(test_dict['config']['qubit_lo_freq'], [4.9, 5.0])
         self.assertEqual(len(test_dict['experiments']), 2)
         self.assertEqual(len(test_dict['experiments'][0]['instructions']), 2)
@@ -432,10 +453,11 @@ def test_assemble_meas_map(self):
         acquire = pulse.Acquire(5)
         schedule = acquire([AcquireChannel(0), AcquireChannel(1)],
                            [MemorySlot(0), MemorySlot(1)])
-        assemble(schedule,
-                 qubit_lo_freq=self.default_qubit_lo_freq,
-                 meas_lo_freq=self.default_meas_lo_freq,
-                 meas_map=[[0], [1]])
+        qobj = assemble(schedule,
+                        qubit_lo_freq=self.default_qubit_lo_freq,
+                        meas_lo_freq=self.default_meas_lo_freq,
+                        meas_map=[[0], [1]])
+        validate_qobj_against_schema(qobj)
 
         with self.assertRaises(QiskitError):
             assemble(schedule,
@@ -456,6 +478,7 @@ def test_assemble_memory_slots(self):
                         qubit_lo_freq=self.default_qubit_lo_freq,
                         meas_lo_freq=self.default_meas_lo_freq,
                         meas_map=[[0], [1]])
+        validate_qobj_against_schema(qobj)
 
         self.assertEqual(qobj.config.memory_slots, n_memoryslots)
         # this should be in experimental header as well
@@ -471,6 +494,7 @@ def test_assemble_memory_slots(self):
                         qubit_lo_freq=self.default_qubit_lo_freq,
                         meas_lo_freq=self.default_meas_lo_freq,
                         meas_map=[[0], [1]])
+        validate_qobj_against_schema(qobj)
 
         self.assertEqual(qobj.config.memory_slots, n_memoryslots)
         # this should be in experimental header as well
@@ -491,6 +515,7 @@ def test_assemble_memory_slots_for_schedules(self):
                         qubit_lo_freq=self.default_qubit_lo_freq,
                         meas_lo_freq=self.default_meas_lo_freq,
                         meas_map=[[0], [1]])
+        validate_qobj_against_schema(qobj)
 
         self.assertEqual(qobj.config.memory_slots, max(n_memoryslots))
         self.assertEqual(qobj.experiments[0].header.memory_slots, n_memoryslots[0])
@@ -510,6 +535,7 @@ def test_pulse_name_conflicts(self):
                         meas_lo_freq=self.default_meas_lo_freq,
                         schedule_los=[],
                         **self.config)
+        validate_qobj_against_schema(qobj)
 
         self.assertNotEqual(qobj.config.pulse_library[1], 'pulse0')
         self.assertEqual(qobj.experiments[0].instructions[0].name, 'pulse0')
@@ -523,11 +549,28 @@ def test_assemble_with_delay(self):
         delay_schedule = orig_schedule + pulse.Delay(10)(self.backend_config.drive(0))
 
         orig_qobj = assemble(orig_schedule, backend)
+        validate_qobj_against_schema(orig_qobj)
         delay_qobj = assemble(delay_schedule, backend)
+        validate_qobj_against_schema(delay_qobj)
 
         self.assertEqual(orig_qobj.experiments[0].to_dict(),
                          delay_qobj.experiments[0].to_dict())
 
+    def test_assemble_schedule_enum(self):
+        """Test assembling a schedule with enum input values to assemble."""
+        qobj = assemble(self.schedule,
+                        qobj_header=self.header,
+                        qubit_lo_freq=self.default_qubit_lo_freq,
+                        meas_lo_freq=self.default_meas_lo_freq,
+                        schedule_los=[],
+                        meas_level=MeasLevel.CLASSIFIED,
+                        meas_return=MeasReturnType.AVERAGE)
+        validate_qobj_against_schema(qobj)
+
+        test_dict = qobj.to_dict()
+        self.assertEqual(test_dict['config']['meas_return'], 'avg')
+        self.assertEqual(test_dict['config']['meas_level'], 2)
+
 
 class TestPulseAssemblerMissingKwargs(QiskitTestCase):
     """Verify that errors are raised in case backend is not provided and kwargs are missing."""
@@ -553,15 +596,16 @@ def setUp(self):
 
     def test_defaults(self):
         """Test defaults work."""
-        assemble(self.schedule,
-                 qubit_lo_freq=self.qubit_lo_freq,
-                 meas_lo_freq=self.meas_lo_freq,
-                 qubit_lo_range=self.qubit_lo_range,
-                 meas_lo_range=self.meas_lo_range,
-                 schedule_los=self.schedule_los,
-                 meas_map=self.meas_map,
-                 memory_slots=self.memory_slots,
-                 rep_time=self.rep_time)
+        qobj = assemble(self.schedule,
+                        qubit_lo_freq=self.qubit_lo_freq,
+                        meas_lo_freq=self.meas_lo_freq,
+                        qubit_lo_range=self.qubit_lo_range,
+                        meas_lo_range=self.meas_lo_range,
+                        schedule_los=self.schedule_los,
+                        meas_map=self.meas_map,
+                        memory_slots=self.memory_slots,
+                        rep_time=self.rep_time)
+        validate_qobj_against_schema(qobj)
 
     def test_missing_qubit_lo_freq(self):
         """Test error raised if qubit_lo_freq missing."""
@@ -591,54 +635,58 @@ def test_missing_meas_lo_freq(self):
 
     def test_missing_memory_slots(self):
         """Test error is not raised if memory_slots are missing."""
-        assemble(self.schedule,
-                 qubit_lo_freq=self.qubit_lo_freq,
-                 meas_lo_freq=self.meas_lo_freq,
-                 qubit_lo_range=self.qubit_lo_range,
-                 meas_lo_range=self.meas_lo_range,
-                 schedule_los=self.schedule_los,
-                 meas_map=self.meas_map,
-                 memory_slots=None,
-                 rep_time=self.rep_time)
+        qobj = assemble(self.schedule,
+                        qubit_lo_freq=self.qubit_lo_freq,
+                        meas_lo_freq=self.meas_lo_freq,
+                        qubit_lo_range=self.qubit_lo_range,
+                        meas_lo_range=self.meas_lo_range,
+                        schedule_los=self.schedule_los,
+                        meas_map=self.meas_map,
+                        memory_slots=None,
+                        rep_time=self.rep_time)
+        validate_qobj_against_schema(qobj)
 
     def test_missing_rep_time(self):
         """Test that assembly still works if rep_time is missing.
 
         The case of no rep_time will exist for a simulator.
         """
-        assemble(self.schedule,
-                 qubit_lo_freq=self.qubit_lo_freq,
-                 meas_lo_freq=self.meas_lo_freq,
-                 qubit_lo_range=self.qubit_lo_range,
-                 meas_lo_range=self.meas_lo_range,
-                 schedule_los=self.schedule_los,
-                 meas_map=self.meas_map,
-                 memory_slots=self.memory_slots,
-                 rep_time=None)
+        qobj = assemble(self.schedule,
+                        qubit_lo_freq=self.qubit_lo_freq,
+                        meas_lo_freq=self.meas_lo_freq,
+                        qubit_lo_range=self.qubit_lo_range,
+                        meas_lo_range=self.meas_lo_range,
+                        schedule_los=self.schedule_los,
+                        meas_map=self.meas_map,
+                        memory_slots=self.memory_slots,
+                        rep_time=None)
+        validate_qobj_against_schema(qobj)
 
     def test_missing_meas_map(self):
         """Test that assembly still works if meas_map is missing."""
-        assemble(self.schedule,
-                 qubit_lo_freq=self.qubit_lo_freq,
-                 meas_lo_freq=self.meas_lo_freq,
-                 qubit_lo_range=self.qubit_lo_range,
-                 meas_lo_range=self.meas_lo_range,
-                 schedule_los=self.schedule_los,
-                 meas_map=None,
-                 memory_slots=self.memory_slots,
-                 rep_time=self.rep_time)
+        qobj = assemble(self.schedule,
+                        qubit_lo_freq=self.qubit_lo_freq,
+                        meas_lo_freq=self.meas_lo_freq,
+                        qubit_lo_range=self.qubit_lo_range,
+                        meas_lo_range=self.meas_lo_range,
+                        schedule_los=self.schedule_los,
+                        meas_map=None,
+                        memory_slots=self.memory_slots,
+                        rep_time=self.rep_time)
+        validate_qobj_against_schema(qobj)
 
     def test_missing_lo_ranges(self):
         """Test that assembly still works if lo_ranges are missing."""
-        assemble(self.schedule,
-                 qubit_lo_freq=self.qubit_lo_freq,
-                 meas_lo_freq=self.meas_lo_freq,
-                 qubit_lo_range=None,
-                 meas_lo_range=None,
-                 schedule_los=self.schedule_los,
-                 meas_map=self.meas_map,
-                 memory_slots=self.memory_slots,
-                 rep_time=self.rep_time)
+        qobj = assemble(self.schedule,
+                        qubit_lo_freq=self.qubit_lo_freq,
+                        meas_lo_freq=self.meas_lo_freq,
+                        qubit_lo_range=None,
+                        meas_lo_range=None,
+                        schedule_los=self.schedule_los,
+                        meas_map=self.meas_map,
+                        memory_slots=self.memory_slots,
+                        rep_time=self.rep_time)
+        validate_qobj_against_schema(qobj)
 
 
 if __name__ == '__main__':