Repository: Qiskit/qiskit

Issue:
result.get_counts() should return all counts if called with no arg
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?
`job.result().get_counts()` should return a list of all the counts available when there are multiple circuits in a job.

This allows, for example, doing:
```
plot_histogram(result.get_counts())
```
verses having to explicitly write out the counts indexing by hand, or in a loop using `len(job.result().results)` and appending to a new list.



Discussion:
Hello, I am quite new to qiskit but I wanted to face this issue. May you be more precise on how to reproduce the problem ? 

If i understood correctly you execute a job with multiple circuits and then you wanted to plot the results of all the circuits with the single method plot_histogram.

I looked into __result.py__ and I think that trying to do:
`plot_histogram(job.result().get_counts())`
would raise the error:
`You have to select a circuit or schedule when there is more than one available`

Is this the enhancement needed ?
The enhancement is that result.get_counts() should return a list of dict containing counts for all the experiments in the result. You are correct that it currently errors. 
Ok I would like to get into it then. 

Changing the get_counts() method to always return a list of dict would need many updates over the other classes using this method ? In this way in the case of a single experiment we will have a list of a single element.

If you think that this solution works I can start dealing with it.
The single circuit case should not change
This would be much simpler if a result had an attribute `counts` and then instead of:
- `result.get_counts()` we have `result.counts`
- `result.get_counts(i)` we have `result.counts[i]`
But isn't a method needed also to possibly specify the experiment?

The method in fact is defined as:

`get_counts(self, experiment=None)`  
That is a good point, the method is doing several things.

On Wed, Jan 22, 2020, 12:18 PM Francesco Piro <notifications@github.com>
wrote:

> But isn't a method needed also to possibly specify the experiment?
>
> The method in fact is defined as:
>
> get_counts(self, experiment=None)
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/Qiskit/qiskit-terra/issues/3733?email_source=notifications&email_token=AAL3LNVBUQ6JWO5FAVYCKJDQ7B5VRA5CNFSM4KJPXHD2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJUMOYY#issuecomment-577292131>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAL3LNWCJKL6CYV6HI6QA7TQ7B5VRANCNFSM4KJPXHDQ>
> .
>

You are right, for this reason I was thinking that maybe it is better to keep it.

I did a pull request #3737 that fixes this issue, if you could check, it would be great as the enhancement will be achieved!

Resolving patch:
diff --git a/qiskit/result/result.py b/qiskit/result/result.py
--- a/qiskit/result/result.py
+++ b/qiskit/result/result.py
@@ -165,7 +165,8 @@ def get_counts(self, experiment=None):
                 experiment, as specified by ``get_data()``.
 
         Returns:
-            dict[str:int]: a dictionary with the counts for each qubit, with
+            dict[str:int] or list[dict[str:int]]: a dictionary or a list of
+                dictionaries. A dictionary has the counts for each qubit with
                 the keys containing a string in binary format and separated
                 according to the registers in circuit (e.g. ``0100 1110``).
                 The string is little-endian (cr[0] on the right hand side).
@@ -173,20 +174,32 @@ def get_counts(self, experiment=None):
         Raises:
             QiskitError: if there are no counts for the experiment.
         """
-        exp = self._get_experiment(experiment)
-        try:
-            header = exp.header.to_dict()
-        except (AttributeError, QiskitError):  # header is not available
-            header = None
-
-        if 'counts' in self.data(experiment).keys():
-            return postprocess.format_counts(self.data(experiment)['counts'],
-                                             header)
-        elif 'statevector' in self.data(experiment).keys():
-            vec = postprocess.format_statevector(self.data(experiment)['statevector'])
-            return state_to_counts(vec)
+        if experiment is None:
+            exp_keys = range(len(self.results))
+        else:
+            exp_keys = [experiment]
+
+        dict_list = []
+        for key in exp_keys:
+            exp = self._get_experiment(key)
+            try:
+                header = exp.header.to_dict()
+            except (AttributeError, QiskitError):  # header is not available
+                header = None
+
+            if 'counts' in self.data(key).keys():
+                dict_list.append(postprocess.format_counts(self.data(key)['counts'], header))
+            elif 'statevector' in self.data(key).keys():
+                vec = postprocess.format_statevector(self.data(key)['statevector'])
+                dict_list.append(state_to_counts(vec))
+            else:
+                raise QiskitError('No counts for experiment "{0}"'.format(key))
+
+        # Return first item of dict_list if size is 1
+        if len(dict_list) == 1:
+            return dict_list[0]
         else:
-            raise QiskitError('No counts for experiment "{0}"'.format(experiment))
+            return dict_list
 
     def get_statevector(self, experiment=None, decimals=None):
         """Get the final statevector of an experiment.


Test patch:
diff --git a/test/python/result/test_result.py b/test/python/result/test_result.py
--- a/test/python/result/test_result.py
+++ b/test/python/result/test_result.py
@@ -59,6 +59,44 @@ def test_counts_header(self):
 
         self.assertEqual(result.get_counts(0), processed_counts)
 
+    def test_multiple_circuits_counts(self):
+        """"
+        Test that counts are returned either as a list or a single item.
+
+        Counts are returned as a list when multiple experiments are executed
+        and get_counts() is called with no arguments. In all the other cases
+        get_counts() returns a single item containing the counts for a
+        single experiment.
+        """
+        raw_counts_1 = {'0x0': 5, '0x3': 12, '0x5': 9, '0xD': 6, '0xE': 2}
+        processed_counts_1 = {'0000': 5, '0011': 12, '0101': 9, '1101': 6, '1110': 2}
+        data_1 = models.ExperimentResultData(counts=base.Obj(**raw_counts_1))
+        exp_result_header_1 = base.Obj(creg_sizes=[['c0', 4]], memory_slots=4)
+        exp_result_1 = models.ExperimentResult(shots=14, success=True, meas_level=2, data=data_1,
+                                               header=exp_result_header_1)
+
+        raw_counts_2 = {'0x1': 0, '0x4': 3, '0x6': 6, '0xA': 1, '0xB': 2}
+        processed_counts_2 = {'0001': 0, '0100': 3, '0110': 6, '1010': 1, '1011': 2}
+        data_2 = models.ExperimentResultData(counts=base.Obj(**raw_counts_2))
+        exp_result_header_2 = base.Obj(creg_sizes=[['c0', 4]], memory_slots=4)
+        exp_result_2 = models.ExperimentResult(shots=14, success=True, meas_level=2, data=data_2,
+                                               header=exp_result_header_2)
+
+        raw_counts_3 = {'0xC': 27, '0xF': 20}
+        processed_counts_3 = {'1100': 27, '1111': 20}
+        data_3 = models.ExperimentResultData(counts=base.Obj(**raw_counts_3))
+        exp_result_header_3 = base.Obj(creg_sizes=[['c0', 4]], memory_slots=4)
+        exp_result_3 = models.ExperimentResult(shots=14, success=True, meas_level=2, data=data_3,
+                                               header=exp_result_header_3)
+
+        mult_result = Result(results=[exp_result_1, exp_result_2, exp_result_3],
+                             **self.base_result_args)
+        sing_result = Result(results=[exp_result_1], **self.base_result_args)
+
+        self.assertEqual(mult_result.get_counts(), [processed_counts_1, processed_counts_2,
+                                                    processed_counts_3])
+        self.assertEqual(sing_result.get_counts(), processed_counts_1)
+
     def test_marginal_counts(self):
         """Test that counts are marginalized correctly."""
         raw_counts = {'0x0': 4, '0x1': 7, '0x2': 10, '0x6': 5, '0x9': 11, '0xD': 9, '0xE': 8}