We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
marginal_counts is broken for pulse results
<!-- ⚠️ 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**: 0.18.0
- **Python version**: 3.9
- **Operating system**: OSx

### What is the current behavior?
`marginal_counts` is broken for pulse jobs. I


### Steps to reproduce the problem
```python
from qiskit import pulse, QuantumCircuit, schedule, transpile, execute, IBMQ
from qiskit.result import marginal_counts


IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')
backend = provider.get_backend('ibmq_armonk')

qc = QuantumCircuit(1,1)
qc.measure(0, 0)

job = backend.run(schedule(transpile(qc, backend), backend))
marginal_counts(job.result(), indices=[0])
```
Gives output
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-8e897d2c2a54> in <module>
     11 
     12 job = backend.run(schedule(transpile(qc, backend), backend))
---> 13 marginal_counts(job.result(), indices=[0])

~/opt/anaconda3/envs/python39/lib/python3.9/site-packages/qiskit/result/utils.py in marginal_counts(result, indices, inplace, format_marginal)
     55             experiment_result.data.counts = new_counts_hex
     56             experiment_result.header.memory_slots = len(indices)
---> 57             csize = experiment_result.header.creg_sizes
     58             experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)
     59         return result

AttributeError: 'QobjExperimentHeader' object has no attribute 'creg_sizes'
```


### What is the expected behavior?



### Suggested solutions
It seems like it is assuming `creg` will be present in the header. This is not true for pulse jobs which do not have classical registers and therefore this field won't be present. The fix should make marginal_counts work when the header is empty.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qiskit/result/utils.py`
Content:
```
1 # This code is part of Qiskit.
2 #
3 # (C) Copyright IBM 2017, 2018.
4 #
5 # This code is licensed under the Apache License, Version 2.0. You may
6 # obtain a copy of this license in the LICENSE.txt file in the root directory
7 # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8 #
9 # Any modifications or derivative works of this code must retain this
10 # copyright notice, and modified files need to carry a notice indicating
11 # that they have been altered from the originals.
12 
13 # pylint: disable=c-extension-no-member
14 
15 """Utility functions for working with Results."""
16 
17 from typing import List, Union, Optional, Dict
18 from collections import Counter
19 from copy import deepcopy
20 
21 from qiskit.exceptions import QiskitError
22 from qiskit.result.result import Result
23 from qiskit.result.counts import Counts
24 from qiskit.result.distributions.probability import ProbDistribution
25 from qiskit.result.distributions.quasi import QuasiDistribution
26 
27 from qiskit.result.postprocess import _bin_to_hex, _hex_to_bin
28 
29 # pylint: disable=import-error, no-name-in-module
30 from qiskit._accelerate import results as results_rs
31 
32 
33 def marginal_counts(
34     result: Union[dict, Result],
35     indices: Optional[List[int]] = None,
36     inplace: bool = False,
37     format_marginal: bool = False,
38     marginalize_memory: Optional[bool] = True,
39 ) -> Union[Dict[str, int], Result]:
40     """Marginalize counts from an experiment over some indices of interest.
41 
42     Args:
43         result: result to be marginalized
44             (a Result object or a dict(str, int) of counts).
45         indices: The bit positions of interest
46             to marginalize over. If ``None`` (default), do not marginalize at all.
47         inplace: Default: False. Operates on the original Result
48             argument if True, leading to loss of original Job Result.
49             It has no effect if ``result`` is a dict.
50         format_marginal: Default: False. If True, takes the output of
51             marginalize and formats it with placeholders between cregs and
52             for non-indices.
53         marginalize_memory: If True, then also marginalize the memory field (if present).
54             If False, remove the memory field from the result.
55             If None, leave the memory field as is.
56 
57     Returns:
58         Result or dict(str, int): A Result object or a dictionary with
59             the observed counts, marginalized to only account for frequency
60             of observations of bits of interest.
61 
62     Raises:
63         QiskitError: in case of invalid indices to marginalize over.
64     """
65     if isinstance(result, Result):
66         if not inplace:
67             result = deepcopy(result)
68         for i, experiment_result in enumerate(result.results):
69             counts = result.get_counts(i)
70             new_counts = _marginalize(counts, indices)
71             new_counts_hex = {}
72             for k, v in new_counts.items():
73                 new_counts_hex[_bin_to_hex(k)] = v
74             experiment_result.data.counts = new_counts_hex
75 
76             if indices is not None:
77                 experiment_result.header.memory_slots = len(indices)
78                 csize = experiment_result.header.creg_sizes
79                 experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)
80 
81             if getattr(experiment_result.data, "memory", None) is not None and indices is not None:
82                 if marginalize_memory is False:
83                     delattr(experiment_result.data, "memory")
84                 elif marginalize_memory is None:
85                     pass  # leave as is
86                 else:
87                     sorted_indices = sorted(
88                         indices, reverse=True
89                     )  # same convention as for the counts
90                     bit_strings = [_hex_to_bin(s) for s in experiment_result.data.memory]
91                     marginal_bit_strings = [
92                         "".join([s[-idx - 1] for idx in sorted_indices if idx < len(s)]) or "0"
93                         for s in bit_strings
94                     ]
95                     experiment_result.data.memory = [_bin_to_hex(s) for s in marginal_bit_strings]
96         return result
97     else:
98         marg_counts = _marginalize(result, indices)
99         if format_marginal and indices is not None:
100             marg_counts = _format_marginal(result, marg_counts, indices)
101         return marg_counts
102 
103 
104 def _adjust_creg_sizes(creg_sizes, indices):
105     """Helper to reduce creg_sizes to match indices"""
106 
107     # Zero out creg_sizes list
108     new_creg_sizes = [[creg[0], 0] for creg in creg_sizes]
109     indices_sort = sorted(indices)
110 
111     # Get creg num values and then convert to the cumulative last index per creg.
112     # e.g. [2, 1, 3] => [1, 2, 5]
113     creg_nums = [x for _, x in creg_sizes]
114     creg_limits = [sum(creg_nums[0:x:1]) - 1 for x in range(0, len(creg_nums) + 1)][1:]
115 
116     # Now iterate over indices and find which creg that index is in.
117     # When found increment the creg size
118     creg_idx = 0
119     for ind in indices_sort:
120         for idx in range(creg_idx, len(creg_limits)):
121             if ind <= creg_limits[idx]:
122                 creg_idx = idx
123                 new_creg_sizes[idx][1] += 1
124                 break
125     # Throw away any cregs with 0 size
126     new_creg_sizes = [creg for creg in new_creg_sizes if creg[1] != 0]
127     return new_creg_sizes
128 
129 
130 def marginal_distribution(
131     counts: dict, indices: Optional[List[int]] = None, format_marginal: bool = False
132 ) -> Dict[str, int]:
133     """Marginalize counts from an experiment over some indices of interest.
134 
135     Unlike :func:`~.marginal_counts` this function respects the order of
136     the input ``indices``. If the input ``indices`` list is specified, the order
137     the bit indices will be the output order of the bitstrings
138     in the marginalized output.
139 
140     Args:
141         counts: result to be marginalized
142         indices: The bit positions of interest
143             to marginalize over. If ``None`` (default), do not marginalize at all.
144         format_marginal: Default: False. If True, takes the output of
145             marginalize and formats it with placeholders between cregs and
146             for non-indices.
147     Returns:
148         dict(str, int): A marginalized dictionary
149     Raises:
150         QiskitError: If any value in ``indices`` is invalid or the ``counts`` dict
151         is invalid.
152     """
153     num_clbits = len(max(counts.keys()).replace(" ", ""))
154     if indices is not None and (not indices or not set(indices).issubset(range(num_clbits))):
155         raise QiskitError(f"indices must be in range [0, {num_clbits - 1}].")
156 
157     if isinstance(counts, Counts):
158         res = results_rs.marginal_counts(counts, indices)
159     elif isinstance(counts, (ProbDistribution, QuasiDistribution)):
160         res = results_rs.marginal_distribution(counts, indices)
161     else:
162         first_value = next(iter(counts.values()))
163         if isinstance(first_value, int):
164             res = results_rs.marginal_counts(counts, indices)
165         elif isinstance(first_value, float):
166             res = results_rs.marginal_distribution(counts, indices)
167         else:
168             raise QiskitError("Values of counts must be an int or float")
169 
170     if format_marginal and indices is not None:
171         return _format_marginal(counts, res, indices)
172     return res
173 
174 
175 def _marginalize(counts, indices=None):
176     """Get the marginal counts for the given set of indices"""
177     num_clbits = len(next(iter(counts)).replace(" ", ""))
178     # Check if we do not need to marginalize and if so, trim
179     # whitespace and '_' and return
180     if (indices is None) or set(range(num_clbits)) == set(indices):
181         ret = {}
182         for key, val in counts.items():
183             key = _remove_space_underscore(key)
184             ret[key] = val
185         return ret
186 
187     if not indices or not set(indices).issubset(set(range(num_clbits))):
188         raise QiskitError(f"indices must be in range [0, {num_clbits - 1}].")
189 
190     # Sort the indices to keep in descending order
191     # Since bitstrings have qubit-0 as least significant bit
192     indices = sorted(indices, reverse=True)
193 
194     # Build the return list
195     new_counts = Counter()
196     for key, val in counts.items():
197         new_key = "".join([_remove_space_underscore(key)[-idx - 1] for idx in indices])
198         new_counts[new_key] += val
199     return dict(new_counts)
200 
201 
202 def _format_marginal(counts, marg_counts, indices):
203     """Take the output of marginalize and add placeholders for
204     multiple cregs and non-indices."""
205     format_counts = {}
206     counts_template = next(iter(counts))
207     counts_len = len(counts_template.replace(" ", ""))
208     indices_rev = sorted(indices, reverse=True)
209 
210     for count in marg_counts:
211         index_dict = dict(zip(indices_rev, count))
212         count_bits = "".join(
213             [index_dict[index] if index in index_dict else "_" for index in range(counts_len)]
214         )[::-1]
215         for index, bit in enumerate(counts_template):
216             if bit == " ":
217                 count_bits = count_bits[:index] + " " + count_bits[index:]
218         format_counts[count_bits] = marg_counts[count]
219     return format_counts
220 
221 
222 def _remove_space_underscore(bitstring):
223     """Removes all spaces and underscores from bitstring"""
224     return bitstring.replace(" ", "").replace("_", "")
225
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qiskit/result/utils.py b/qiskit/result/utils.py
--- a/qiskit/result/utils.py
+++ b/qiskit/result/utils.py
@@ -75,8 +75,9 @@
 
             if indices is not None:
                 experiment_result.header.memory_slots = len(indices)
-                csize = experiment_result.header.creg_sizes
-                experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)
+                csize = getattr(experiment_result.header, "creg_sizes", None)
+                if csize is not None:
+                    experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)
 
             if getattr(experiment_result.data, "memory", None) is not None and indices is not None:
                 if marginalize_memory is False:

{"golden_diff": "diff --git a/qiskit/result/utils.py b/qiskit/result/utils.py\n--- a/qiskit/result/utils.py\n+++ b/qiskit/result/utils.py\n@@ -75,8 +75,9 @@\n \n             if indices is not None:\n                 experiment_result.header.memory_slots = len(indices)\n-                csize = experiment_result.header.creg_sizes\n-                experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)\n+                csize = getattr(experiment_result.header, \"creg_sizes\", None)\n+                if csize is not None:\n+                    experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)\n \n             if getattr(experiment_result.data, \"memory\", None) is not None and indices is not None:\n                 if marginalize_memory is False:\n", "issue": "marginal_counts is broken for pulse results\n<!-- \u26a0\ufe0f If you do not respect this template, your issue will be closed -->\r\n<!-- \u26a0\ufe0f Make sure to browse the opened and closed issues -->\r\n\r\n### Information\r\n\r\n- **Qiskit Terra version**: 0.18.0\r\n- **Python version**: 3.9\r\n- **Operating system**: OSx\r\n\r\n### What is the current behavior?\r\n`marginal_counts` is broken for pulse jobs. I\r\n\r\n\r\n### Steps to reproduce the problem\r\n```python\r\nfrom qiskit import pulse, QuantumCircuit, schedule, transpile, execute, IBMQ\r\nfrom qiskit.result import marginal_counts\r\n\r\n\r\nIBMQ.load_account()\r\nprovider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')\r\nbackend = provider.get_backend('ibmq_armonk')\r\n\r\nqc = QuantumCircuit(1,1)\r\nqc.measure(0, 0)\r\n\r\njob = backend.run(schedule(transpile(qc, backend), backend))\r\nmarginal_counts(job.result(), indices=[0])\r\n```\r\nGives output\r\n```\r\n---------------------------------------------------------------------------\r\nAttributeError                            Traceback (most recent call last)\r\n<ipython-input-6-8e897d2c2a54> in <module>\r\n     11 \r\n     12 job = backend.run(schedule(transpile(qc, backend), backend))\r\n---> 13 marginal_counts(job.result(), indices=[0])\r\n\r\n~/opt/anaconda3/envs/python39/lib/python3.9/site-packages/qiskit/result/utils.py in marginal_counts(result, indices, inplace, format_marginal)\r\n     55             experiment_result.data.counts = new_counts_hex\r\n     56             experiment_result.header.memory_slots = len(indices)\r\n---> 57             csize = experiment_result.header.creg_sizes\r\n     58             experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)\r\n     59         return result\r\n\r\nAttributeError: 'QobjExperimentHeader' object has no attribute 'creg_sizes'\r\n```\r\n\r\n\r\n### What is the expected behavior?\r\n\r\n\r\n\r\n### Suggested solutions\r\nIt seems like it is assuming `creg` will be present in the header. This is not true for pulse jobs which do not have classical registers and therefore this field won't be present. The fix should make marginal_counts work when the header is empty.\r\n\n", "before_files": [{"content": "# This code is part of Qiskit.\n#\n# (C) Copyright IBM 2017, 2018.\n#\n# This code is licensed under the Apache License, Version 2.0. You may\n# obtain a copy of this license in the LICENSE.txt file in the root directory\n# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.\n#\n# Any modifications or derivative works of this code must retain this\n# copyright notice, and modified files need to carry a notice indicating\n# that they have been altered from the originals.\n\n# pylint: disable=c-extension-no-member\n\n\"\"\"Utility functions for working with Results.\"\"\"\n\nfrom typing import List, Union, Optional, Dict\nfrom collections import Counter\nfrom copy import deepcopy\n\nfrom qiskit.exceptions import QiskitError\nfrom qiskit.result.result import Result\nfrom qiskit.result.counts import Counts\nfrom qiskit.result.distributions.probability import ProbDistribution\nfrom qiskit.result.distributions.quasi import QuasiDistribution\n\nfrom qiskit.result.postprocess import _bin_to_hex, _hex_to_bin\n\n# pylint: disable=import-error, no-name-in-module\nfrom qiskit._accelerate import results as results_rs\n\n\ndef marginal_counts(\n    result: Union[dict, Result],\n    indices: Optional[List[int]] = None,\n    inplace: bool = False,\n    format_marginal: bool = False,\n    marginalize_memory: Optional[bool] = True,\n) -> Union[Dict[str, int], Result]:\n    \"\"\"Marginalize counts from an experiment over some indices of interest.\n\n    Args:\n        result: result to be marginalized\n            (a Result object or a dict(str, int) of counts).\n        indices: The bit positions of interest\n            to marginalize over. If ``None`` (default), do not marginalize at all.\n        inplace: Default: False. Operates on the original Result\n            argument if True, leading to loss of original Job Result.\n            It has no effect if ``result`` is a dict.\n        format_marginal: Default: False. If True, takes the output of\n            marginalize and formats it with placeholders between cregs and\n            for non-indices.\n        marginalize_memory: If True, then also marginalize the memory field (if present).\n            If False, remove the memory field from the result.\n            If None, leave the memory field as is.\n\n    Returns:\n        Result or dict(str, int): A Result object or a dictionary with\n            the observed counts, marginalized to only account for frequency\n            of observations of bits of interest.\n\n    Raises:\n        QiskitError: in case of invalid indices to marginalize over.\n    \"\"\"\n    if isinstance(result, Result):\n        if not inplace:\n            result = deepcopy(result)\n        for i, experiment_result in enumerate(result.results):\n            counts = result.get_counts(i)\n            new_counts = _marginalize(counts, indices)\n            new_counts_hex = {}\n            for k, v in new_counts.items():\n                new_counts_hex[_bin_to_hex(k)] = v\n            experiment_result.data.counts = new_counts_hex\n\n            if indices is not None:\n                experiment_result.header.memory_slots = len(indices)\n                csize = experiment_result.header.creg_sizes\n                experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)\n\n            if getattr(experiment_result.data, \"memory\", None) is not None and indices is not None:\n                if marginalize_memory is False:\n                    delattr(experiment_result.data, \"memory\")\n                elif marginalize_memory is None:\n                    pass  # leave as is\n                else:\n                    sorted_indices = sorted(\n                        indices, reverse=True\n                    )  # same convention as for the counts\n                    bit_strings = [_hex_to_bin(s) for s in experiment_result.data.memory]\n                    marginal_bit_strings = [\n                        \"\".join([s[-idx - 1] for idx in sorted_indices if idx < len(s)]) or \"0\"\n                        for s in bit_strings\n                    ]\n                    experiment_result.data.memory = [_bin_to_hex(s) for s in marginal_bit_strings]\n        return result\n    else:\n        marg_counts = _marginalize(result, indices)\n        if format_marginal and indices is not None:\n            marg_counts = _format_marginal(result, marg_counts, indices)\n        return marg_counts\n\n\ndef _adjust_creg_sizes(creg_sizes, indices):\n    \"\"\"Helper to reduce creg_sizes to match indices\"\"\"\n\n    # Zero out creg_sizes list\n    new_creg_sizes = [[creg[0], 0] for creg in creg_sizes]\n    indices_sort = sorted(indices)\n\n    # Get creg num values and then convert to the cumulative last index per creg.\n    # e.g. [2, 1, 3] => [1, 2, 5]\n    creg_nums = [x for _, x in creg_sizes]\n    creg_limits = [sum(creg_nums[0:x:1]) - 1 for x in range(0, len(creg_nums) + 1)][1:]\n\n    # Now iterate over indices and find which creg that index is in.\n    # When found increment the creg size\n    creg_idx = 0\n    for ind in indices_sort:\n        for idx in range(creg_idx, len(creg_limits)):\n            if ind <= creg_limits[idx]:\n                creg_idx = idx\n                new_creg_sizes[idx][1] += 1\n                break\n    # Throw away any cregs with 0 size\n    new_creg_sizes = [creg for creg in new_creg_sizes if creg[1] != 0]\n    return new_creg_sizes\n\n\ndef marginal_distribution(\n    counts: dict, indices: Optional[List[int]] = None, format_marginal: bool = False\n) -> Dict[str, int]:\n    \"\"\"Marginalize counts from an experiment over some indices of interest.\n\n    Unlike :func:`~.marginal_counts` this function respects the order of\n    the input ``indices``. If the input ``indices`` list is specified, the order\n    the bit indices will be the output order of the bitstrings\n    in the marginalized output.\n\n    Args:\n        counts: result to be marginalized\n        indices: The bit positions of interest\n            to marginalize over. If ``None`` (default), do not marginalize at all.\n        format_marginal: Default: False. If True, takes the output of\n            marginalize and formats it with placeholders between cregs and\n            for non-indices.\n    Returns:\n        dict(str, int): A marginalized dictionary\n    Raises:\n        QiskitError: If any value in ``indices`` is invalid or the ``counts`` dict\n        is invalid.\n    \"\"\"\n    num_clbits = len(max(counts.keys()).replace(\" \", \"\"))\n    if indices is not None and (not indices or not set(indices).issubset(range(num_clbits))):\n        raise QiskitError(f\"indices must be in range [0, {num_clbits - 1}].\")\n\n    if isinstance(counts, Counts):\n        res = results_rs.marginal_counts(counts, indices)\n    elif isinstance(counts, (ProbDistribution, QuasiDistribution)):\n        res = results_rs.marginal_distribution(counts, indices)\n    else:\n        first_value = next(iter(counts.values()))\n        if isinstance(first_value, int):\n            res = results_rs.marginal_counts(counts, indices)\n        elif isinstance(first_value, float):\n            res = results_rs.marginal_distribution(counts, indices)\n        else:\n            raise QiskitError(\"Values of counts must be an int or float\")\n\n    if format_marginal and indices is not None:\n        return _format_marginal(counts, res, indices)\n    return res\n\n\ndef _marginalize(counts, indices=None):\n    \"\"\"Get the marginal counts for the given set of indices\"\"\"\n    num_clbits = len(next(iter(counts)).replace(\" \", \"\"))\n    # Check if we do not need to marginalize and if so, trim\n    # whitespace and '_' and return\n    if (indices is None) or set(range(num_clbits)) == set(indices):\n        ret = {}\n        for key, val in counts.items():\n            key = _remove_space_underscore(key)\n            ret[key] = val\n        return ret\n\n    if not indices or not set(indices).issubset(set(range(num_clbits))):\n        raise QiskitError(f\"indices must be in range [0, {num_clbits - 1}].\")\n\n    # Sort the indices to keep in descending order\n    # Since bitstrings have qubit-0 as least significant bit\n    indices = sorted(indices, reverse=True)\n\n    # Build the return list\n    new_counts = Counter()\n    for key, val in counts.items():\n        new_key = \"\".join([_remove_space_underscore(key)[-idx - 1] for idx in indices])\n        new_counts[new_key] += val\n    return dict(new_counts)\n\n\ndef _format_marginal(counts, marg_counts, indices):\n    \"\"\"Take the output of marginalize and add placeholders for\n    multiple cregs and non-indices.\"\"\"\n    format_counts = {}\n    counts_template = next(iter(counts))\n    counts_len = len(counts_template.replace(\" \", \"\"))\n    indices_rev = sorted(indices, reverse=True)\n\n    for count in marg_counts:\n        index_dict = dict(zip(indices_rev, count))\n        count_bits = \"\".join(\n            [index_dict[index] if index in index_dict else \"_\" for index in range(counts_len)]\n        )[::-1]\n        for index, bit in enumerate(counts_template):\n            if bit == \" \":\n                count_bits = count_bits[:index] + \" \" + count_bits[index:]\n        format_counts[count_bits] = marg_counts[count]\n    return format_counts\n\n\ndef _remove_space_underscore(bitstring):\n    \"\"\"Removes all spaces and underscores from bitstring\"\"\"\n    return bitstring.replace(\" \", \"\").replace(\"_\", \"\")\n", "path": "qiskit/result/utils.py"}], "after_files": [{"content": "# This code is part of Qiskit.\n#\n# (C) Copyright IBM 2017, 2018.\n#\n# This code is licensed under the Apache License, Version 2.0. You may\n# obtain a copy of this license in the LICENSE.txt file in the root directory\n# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.\n#\n# Any modifications or derivative works of this code must retain this\n# copyright notice, and modified files need to carry a notice indicating\n# that they have been altered from the originals.\n\n# pylint: disable=c-extension-no-member\n\n\"\"\"Utility functions for working with Results.\"\"\"\n\nfrom typing import List, Union, Optional, Dict\nfrom collections import Counter\nfrom copy import deepcopy\n\nfrom qiskit.exceptions import QiskitError\nfrom qiskit.result.result import Result\nfrom qiskit.result.counts import Counts\nfrom qiskit.result.distributions.probability import ProbDistribution\nfrom qiskit.result.distributions.quasi import QuasiDistribution\n\nfrom qiskit.result.postprocess import _bin_to_hex, _hex_to_bin\n\n# pylint: disable=import-error, no-name-in-module\nfrom qiskit._accelerate import results as results_rs\n\n\ndef marginal_counts(\n    result: Union[dict, Result],\n    indices: Optional[List[int]] = None,\n    inplace: bool = False,\n    format_marginal: bool = False,\n    marginalize_memory: Optional[bool] = True,\n) -> Union[Dict[str, int], Result]:\n    \"\"\"Marginalize counts from an experiment over some indices of interest.\n\n    Args:\n        result: result to be marginalized\n            (a Result object or a dict(str, int) of counts).\n        indices: The bit positions of interest\n            to marginalize over. If ``None`` (default), do not marginalize at all.\n        inplace: Default: False. Operates on the original Result\n            argument if True, leading to loss of original Job Result.\n            It has no effect if ``result`` is a dict.\n        format_marginal: Default: False. If True, takes the output of\n            marginalize and formats it with placeholders between cregs and\n            for non-indices.\n        marginalize_memory: If True, then also marginalize the memory field (if present).\n            If False, remove the memory field from the result.\n            If None, leave the memory field as is.\n\n    Returns:\n        Result or dict(str, int): A Result object or a dictionary with\n            the observed counts, marginalized to only account for frequency\n            of observations of bits of interest.\n\n    Raises:\n        QiskitError: in case of invalid indices to marginalize over.\n    \"\"\"\n    if isinstance(result, Result):\n        if not inplace:\n            result = deepcopy(result)\n        for i, experiment_result in enumerate(result.results):\n            counts = result.get_counts(i)\n            new_counts = _marginalize(counts, indices)\n            new_counts_hex = {}\n            for k, v in new_counts.items():\n                new_counts_hex[_bin_to_hex(k)] = v\n            experiment_result.data.counts = new_counts_hex\n\n            if indices is not None:\n                experiment_result.header.memory_slots = len(indices)\n                csize = getattr(experiment_result.header, \"creg_sizes\", None)\n                if csize is not None:\n                    experiment_result.header.creg_sizes = _adjust_creg_sizes(csize, indices)\n\n            if getattr(experiment_result.data, \"memory\", None) is not None and indices is not None:\n                if marginalize_memory is False:\n                    delattr(experiment_result.data, \"memory\")\n                elif marginalize_memory is None:\n                    pass  # leave as is\n                else:\n                    sorted_indices = sorted(\n                        indices, reverse=True\n                    )  # same convention as for the counts\n                    bit_strings = [_hex_to_bin(s) for s in experiment_result.data.memory]\n                    marginal_bit_strings = [\n                        \"\".join([s[-idx - 1] for idx in sorted_indices if idx < len(s)]) or \"0\"\n                        for s in bit_strings\n                    ]\n                    experiment_result.data.memory = [_bin_to_hex(s) for s in marginal_bit_strings]\n        return result\n    else:\n        marg_counts = _marginalize(result, indices)\n        if format_marginal and indices is not None:\n            marg_counts = _format_marginal(result, marg_counts, indices)\n        return marg_counts\n\n\ndef _adjust_creg_sizes(creg_sizes, indices):\n    \"\"\"Helper to reduce creg_sizes to match indices\"\"\"\n\n    # Zero out creg_sizes list\n    new_creg_sizes = [[creg[0], 0] for creg in creg_sizes]\n    indices_sort = sorted(indices)\n\n    # Get creg num values and then convert to the cumulative last index per creg.\n    # e.g. [2, 1, 3] => [1, 2, 5]\n    creg_nums = [x for _, x in creg_sizes]\n    creg_limits = [sum(creg_nums[0:x:1]) - 1 for x in range(0, len(creg_nums) + 1)][1:]\n\n    # Now iterate over indices and find which creg that index is in.\n    # When found increment the creg size\n    creg_idx = 0\n    for ind in indices_sort:\n        for idx in range(creg_idx, len(creg_limits)):\n            if ind <= creg_limits[idx]:\n                creg_idx = idx\n                new_creg_sizes[idx][1] += 1\n                break\n    # Throw away any cregs with 0 size\n    new_creg_sizes = [creg for creg in new_creg_sizes if creg[1] != 0]\n    return new_creg_sizes\n\n\ndef marginal_distribution(\n    counts: dict, indices: Optional[List[int]] = None, format_marginal: bool = False\n) -> Dict[str, int]:\n    \"\"\"Marginalize counts from an experiment over some indices of interest.\n\n    Unlike :func:`~.marginal_counts` this function respects the order of\n    the input ``indices``. If the input ``indices`` list is specified, the order\n    the bit indices will be the output order of the bitstrings\n    in the marginalized output.\n\n    Args:\n        counts: result to be marginalized\n        indices: The bit positions of interest\n            to marginalize over. If ``None`` (default), do not marginalize at all.\n        format_marginal: Default: False. If True, takes the output of\n            marginalize and formats it with placeholders between cregs and\n            for non-indices.\n    Returns:\n        dict(str, int): A marginalized dictionary\n    Raises:\n        QiskitError: If any value in ``indices`` is invalid or the ``counts`` dict\n        is invalid.\n    \"\"\"\n    num_clbits = len(max(counts.keys()).replace(\" \", \"\"))\n    if indices is not None and (not indices or not set(indices).issubset(range(num_clbits))):\n        raise QiskitError(f\"indices must be in range [0, {num_clbits - 1}].\")\n\n    if isinstance(counts, Counts):\n        res = results_rs.marginal_counts(counts, indices)\n    elif isinstance(counts, (ProbDistribution, QuasiDistribution)):\n        res = results_rs.marginal_distribution(counts, indices)\n    else:\n        first_value = next(iter(counts.values()))\n        if isinstance(first_value, int):\n            res = results_rs.marginal_counts(counts, indices)\n        elif isinstance(first_value, float):\n            res = results_rs.marginal_distribution(counts, indices)\n        else:\n            raise QiskitError(\"Values of counts must be an int or float\")\n\n    if format_marginal and indices is not None:\n        return _format_marginal(counts, res, indices)\n    return res\n\n\ndef _marginalize(counts, indices=None):\n    \"\"\"Get the marginal counts for the given set of indices\"\"\"\n    num_clbits = len(next(iter(counts)).replace(\" \", \"\"))\n    # Check if we do not need to marginalize and if so, trim\n    # whitespace and '_' and return\n    if (indices is None) or set(range(num_clbits)) == set(indices):\n        ret = {}\n        for key, val in counts.items():\n            key = _remove_space_underscore(key)\n            ret[key] = val\n        return ret\n\n    if not indices or not set(indices).issubset(set(range(num_clbits))):\n        raise QiskitError(f\"indices must be in range [0, {num_clbits - 1}].\")\n\n    # Sort the indices to keep in descending order\n    # Since bitstrings have qubit-0 as least significant bit\n    indices = sorted(indices, reverse=True)\n\n    # Build the return list\n    new_counts = Counter()\n    for key, val in counts.items():\n        new_key = \"\".join([_remove_space_underscore(key)[-idx - 1] for idx in indices])\n        new_counts[new_key] += val\n    return dict(new_counts)\n\n\ndef _format_marginal(counts, marg_counts, indices):\n    \"\"\"Take the output of marginalize and add placeholders for\n    multiple cregs and non-indices.\"\"\"\n    format_counts = {}\n    counts_template = next(iter(counts))\n    counts_len = len(counts_template.replace(\" \", \"\"))\n    indices_rev = sorted(indices, reverse=True)\n\n    for count in marg_counts:\n        index_dict = dict(zip(indices_rev, count))\n        count_bits = \"\".join(\n            [index_dict[index] if index in index_dict else \"_\" for index in range(counts_len)]\n        )[::-1]\n        for index, bit in enumerate(counts_template):\n            if bit == \" \":\n                count_bits = count_bits[:index] + \" \" + count_bits[index:]\n        format_counts[count_bits] = marg_counts[count]\n    return format_counts\n\n\ndef _remove_space_underscore(bitstring):\n    \"\"\"Removes all spaces and underscores from bitstring\"\"\"\n    return bitstring.replace(\" \", \"\").replace(\"_\", \"\")\n", "path": "qiskit/result/utils.py"}]}