Repository: Qiskit/qiskit

Issue:
Pulse meas_level=2 result doesn't conduct zero padding
<!-- ⚠️ 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?
Experimental result with `meas_level=2` doesn't conduct zero padding.


### Steps to reproduce the problem
```
result.get_counts(0)
```
it may return
```
{'1000010000': 1,
 '10000000010000000011': 1,
 '10000010010010': 1,
 '1000000100': 1,
 '1000011': 1,
 '10011': 4,
 '10000100': 1,
 '10000000010010': 2,
 '100000': 1,
 '100000000': 5,
 '10100': 1,
 '10000100001000000010': 
...
```


### What is the expected behavior?
the result bit streams should have the same length (length of `memory_slots`).

### Suggested solutions
`qiskit.result` sees `QobjExperimentalHeader` to find `memory_slots`.

 https://github.com/Qiskit/qiskit-terra/blob/f54f02d151ed9b671e45da13cf20d22ccbb2d28d/qiskit/result/result.py#L135-L140

`memory_slots` should be added to the `QobjExperimentalHeader`.


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
@@ -53,8 +53,6 @@ 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)
 
-    max_memory_slot = 0
-
     instruction_converter = instruction_converter(PulseQobjInstruction, **qobj_config)
 
     lo_converter = LoConfigConverter(PulseQobjExperimentConfig,
@@ -62,12 +60,16 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
                                      meas_lo_range=meas_lo_range,
                                      **qobj_config)
 
+    memory_slot_size = 0
+
     # Pack everything into the Qobj
     qobj_schedules = []
     user_pulselib = {}
     for idx, schedule in enumerate(schedules):
         # instructions
+        max_memory_slot = 0
         qobj_instructions = []
+
         # Instructions are returned as tuple of shifted time and instruction
         for shift, instruction in schedule.instructions:
             # TODO: support conditional gate
@@ -86,7 +88,6 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
                         channel=instruction.channels[0])
                 # add samples to pulse library
                 user_pulselib[name] = instruction.command
-
             elif isinstance(instruction, AcquireInstruction):
                 max_memory_slot = max(max_memory_slot,
                                       *[slot.index for slot in instruction.mem_slots])
@@ -97,8 +98,14 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
             converted_instruction = instruction_converter(shift, instruction)
             qobj_instructions.append(converted_instruction)
 
+        # memory slot size is memory slot index + 1 because index starts from zero
+        exp_memory_slot_size = max_memory_slot + 1
+        memory_slot_size = max(memory_slot_size, exp_memory_slot_size)
+
         # experiment header
+        # TODO: add other experimental header items (see circuit assembler)
         qobj_experiment_header = QobjExperimentHeader(
+            memory_slots=exp_memory_slot_size,
             name=schedule.name or 'Experiment-%d' % idx
         )
 
@@ -108,7 +115,7 @@ def assemble_schedules(schedules, qobj_id, qobj_header, run_config):
         })
 
     # set number of memoryslots
-    qobj_config['memory_slots'] = max_memory_slot + 1
+    qobj_config['memory_slots'] = memory_slot_size
 
     # setup pulse_library
     qobj_config['pulse_library'] = [PulseLibraryItem(name=pulse.name, samples=pulse.samples)


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
@@ -454,6 +454,8 @@ def test_assemble_memory_slots(self):
                         meas_map=[[0], [1]])
 
         self.assertEqual(qobj.config.memory_slots, n_memoryslots)
+        # this should be in experimental header as well
+        self.assertEqual(qobj.experiments[0].header.memory_slots, n_memoryslots)
 
         # multiple acquisition
         schedule = acquire(self.device.acquires[0], mem_slots=pulse.MemorySlot(n_memoryslots-1))
@@ -466,6 +468,28 @@ def test_assemble_memory_slots(self):
                         meas_map=[[0], [1]])
 
         self.assertEqual(qobj.config.memory_slots, n_memoryslots)
+        # this should be in experimental header as well
+        self.assertEqual(qobj.experiments[0].header.memory_slots, n_memoryslots)
+
+    def test_assemble_memory_slots_for_schedules(self):
+        """Test assembling schedules with different memory slots."""
+        acquire = pulse.Acquire(5)
+        n_memoryslots = [10, 5, 7]
+
+        schedules = []
+        for n_memoryslot in n_memoryslots:
+            schedule = acquire(self.device.acquires[0], mem_slots=pulse.MemorySlot(n_memoryslot-1))
+            schedules.append(schedule)
+
+        qobj = assemble(schedules,
+                        qubit_lo_freq=self.default_qubit_lo_freq,
+                        meas_lo_freq=self.default_meas_lo_freq,
+                        meas_map=[[0], [1]])
+
+        self.assertEqual(qobj.config.memory_slots, max(n_memoryslots))
+        self.assertEqual(qobj.experiments[0].header.memory_slots, n_memoryslots[0])
+        self.assertEqual(qobj.experiments[1].header.memory_slots, n_memoryslots[1])
+        self.assertEqual(qobj.experiments[2].header.memory_slots, n_memoryslots[2])
 
     def test_pulse_name_conflicts(self):
         """Test that pulse name conflicts can be resolved."""