Repository: Qiskit/qiskit

Issue:
Classical bit string in strange format for real device.
I have a three bit classical register and receive a 5 bit string as output. This always seems to include two bit that are always 0, but they aren't always in the same place. I cannot determine the logic behind it.

I think it related to, but not exactly the same as, this issue
https://github.com/IBM/qiskit-sdk-py/issues/2

More info can be found in my post to the QX community
https://quantumexperience.ng.bluemix.net/qstage/#/community/question?questionId=3fde5ace6866eb41eba24fae2ce4dce3


Discussion:
@decodoku yeah its the same error the real device returns 5 bits and the order is q4,q3,q2,q1,q0 so if your using only the first three the first two bits will be zero. 
Sorry for not reading your post in full before commenting above. 

From the example your posted on the qx community.  I see you ran two circuits 

OPENQASM 2.0;
include "qelib1.inc";
qreg q[5];
creg c[3];
u3( 1.0 * pi,0,0) q[1];
measure q[1] -> c[0];
measure q[2] -> c[1];
measure q[3] -> c[2];

giving results 

{'01011': 61, '01000': 1, '01010': 3, '00001': 72, '00011': 789, '01001': 6, '00000': 3, '00010': 89}

and 

OPENQASM 2.0;
include "qelib1.inc";
qreg q[5];
creg c[3];
u3( 0.5 * pi,0,0) q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
measure q[2] -> c[2];

{'00110': 1, '01010': 251, '01000': 273, '00100': 1, '01100': 1, '00000': 255, '00010': 242}

The current bug is if  both of these are run separately then in the first one the qubit 1 and 4 should default to zero and in the second one qubit 3 and 4 should be zero. If run together (in a batch) only qubit 4 will be zero as in the batching we insert measurements that have been declared in any circuit. We have described this somewhere (i forget at the moment). So i agree it is not the same as outline in #2 but its the same bug.

So in summary the bugs in the measurement are 

1. it defaults the classical register to the qubit label. 
2. in a batch all the circuits that have a measurement sets all circuits to measure these qubits. 

I think both of these describe your errors and both we are working to fix. Please let me know if you are observing something different to this and once again thanks for using our device. 

This is a limitation of the current architecture backend and depends on the release of 0.5, when qobj is integrated. Therefore, I'm tagging this issue with "on hold".

Resolving patch:
diff --git a/qiskit/backends/ibmq/ibmqbackend.py b/qiskit/backends/ibmq/ibmqbackend.py
--- a/qiskit/backends/ibmq/ibmqbackend.py
+++ b/qiskit/backends/ibmq/ibmqbackend.py
@@ -120,6 +120,8 @@ def run(self, q_job):
         job_result['name'] = qobj['id']
         job_result['backend'] = qobj['config']['backend_name']
         this_result = Result(job_result, qobj)
+        if not self.configuration['simulator'] and this_result.get_status() == "COMPLETED":
+            _reorder_bits(this_result)  # TODO: remove this after Qobj
         return this_result
 
     @property
@@ -264,3 +266,58 @@ def _wait_for_job(job_id, api, wait=5, timeout=60):
                                   'status': job_result['qasms'][index]['status']})
     return {'job_id': job_id, 'status': job_result['status'],
             'result': job_result_return}
+
+
+def _reorder_bits(result):
+    """temporary fix for ibmq backends.
+    for every ran circuit, get reordering information from qobj
+    and apply reordering on result"""
+    for idx, circ in enumerate(result._qobj['circuits']):
+
+        # device_qubit -> device_clbit (how it should have been)
+        measure_dict = {op['qubits'][0]: op['clbits'][0]
+                        for op in circ['compiled_circuit']['operations']
+                        if op['name'] == 'measure'}
+
+        res = result._result['result'][idx]
+        counts_dict_new = {}
+        for item in res['data']['counts'].items():
+            # fix clbit ordering to what it should have been
+            bits = list(item[0])
+            bits.reverse()  # lsb in 0th position
+            count = item[1]
+            reordered_bits = list('x' * len(bits))
+            for device_clbit, bit in enumerate(bits):
+                if device_clbit in measure_dict:
+                    correct_device_clbit = measure_dict[device_clbit]
+                    reordered_bits[correct_device_clbit] = bit
+            reordered_bits.reverse()
+
+            # only keep the clbits specified by circuit, not everything on device
+            num_clbits = circ['compiled_circuit']['header']['number_of_clbits']
+            compact_key = reordered_bits[-num_clbits:]
+            compact_key = "".join([b if b != 'x' else '0'
+                                   for b in compact_key])
+
+            # insert spaces to signify different classical registers
+            cregs = circ['compiled_circuit']['header']['clbit_labels']
+            if sum([creg[1] for creg in cregs]) != num_clbits:
+                raise ResultError("creg sizes don't add up in result header.")
+            creg_begin_pos = []
+            creg_end_pos = []
+            acc = 0
+            for creg in reversed(cregs):
+                creg_size = creg[1]
+                creg_begin_pos.append(acc)
+                creg_end_pos.append(acc + creg_size)
+                acc += creg_size
+            compact_key = " ".join([compact_key[creg_begin_pos[i]:creg_end_pos[i]]
+                                    for i in range(len(cregs))])
+
+            # marginalize over unwanted measured qubits
+            if compact_key not in counts_dict_new:
+                counts_dict_new[compact_key] = count
+            else:
+                counts_dict_new[compact_key] += count
+
+        res['data']['counts'] = counts_dict_new


Test patch:
diff --git a/test/python/common.py b/test/python/common.py
--- a/test/python/common.py
+++ b/test/python/common.py
@@ -40,6 +40,7 @@ class Path(Enum):
 
 class QiskitTestCase(unittest.TestCase):
     """Helper class that contains common functionality."""
+
     @classmethod
     def setUpClass(cls):
         cls.moduleName = os.path.splitext(inspect.getfile(cls))[0]
@@ -202,6 +203,26 @@ def __exit__(self, exc_type, exc_value, tb):
             self._raiseFailure(msg)
 
 
+def slow_test(func):
+    """
+    Decorator that signals that the test takes minutes to run.
+
+    Args:
+        func (callable): test function to be decorated.
+
+    Returns:
+        callable: the decorated function.
+    """
+
+    @functools.wraps(func)
+    def _(*args, **kwargs):
+        if SKIP_SLOW_TESTS:
+            raise unittest.SkipTest('Skipping slow tests')
+        return func(*args, **kwargs)
+
+    return _
+
+
 def requires_qe_access(func):
     """
     Decorator that signals that the test uses the online API:
@@ -217,6 +238,7 @@ def requires_qe_access(func):
     Returns:
         callable: the decorated function.
     """
+
     @functools.wraps(func)
     def _(*args, **kwargs):
         # pylint: disable=invalid-name
@@ -266,3 +288,4 @@ def _is_ci_fork_pull_request():
 
 
 SKIP_ONLINE_TESTS = os.getenv('SKIP_ONLINE_TESTS', _is_ci_fork_pull_request())
+SKIP_SLOW_TESTS = os.getenv('SKIP_SLOW_TESTS', True) not in ['false', 'False', '-1']
diff --git a/test/python/test_reordering.py b/test/python/test_reordering.py
new file mode 100644
--- /dev/null
+++ b/test/python/test_reordering.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# pylint: disable=invalid-name,no-value-for-parameter,broad-except
+
+# Copyright 2018 IBM RESEARCH. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# =============================================================================
+"""Tests for bit reordering fix."""
+
+import unittest
+import qiskit
+from qiskit.wrapper import register, available_backends, get_backend, execute
+from .common import requires_qe_access, QiskitTestCase, slow_test
+
+
+def lowest_pending_jobs(list_of_backends):
+    """Returns the backend with lowest pending jobs."""
+    by_pending_jobs = sorted(list_of_backends,
+                             key=lambda x: get_backend(x).status['pending_jobs'])
+    return by_pending_jobs[0]
+
+
+class TestBitReordering(QiskitTestCase):
+    """Test QISKit's fix for the ibmq hardware reordering bug.
+
+    The bug will be fixed with the introduction of qobj,
+    in which case these tests can be used to verify correctness.
+    """
+    @slow_test
+    @requires_qe_access
+    def test_basic_reordering(self, QE_TOKEN, QE_URL):
+        """a simple reordering within a 2-qubit register"""
+        sim, real = self._get_backends(QE_TOKEN, QE_URL)
+        unittest.skipIf(not real, 'no remote device available.')
+
+        q = qiskit.QuantumRegister(2)
+        c = qiskit.ClassicalRegister(2)
+        circ = qiskit.QuantumCircuit(q, c)
+        circ.h(q[0])
+        circ.measure(q[0], c[1])
+        circ.measure(q[1], c[0])
+
+        shots = 2000
+        result_real = execute(circ, real, {"shots": shots}, timeout=600)
+        result_sim = execute(circ, sim, {"shots": shots})
+        counts_real = result_real.get_counts()
+        counts_sim = result_sim.get_counts()
+        threshold = 0.1 * shots
+        self.assertDictAlmostEqual(counts_real, counts_sim, threshold)
+
+    @slow_test
+    @requires_qe_access
+    def test_multi_register_reordering(self, QE_TOKEN, QE_URL):
+        """a more complicated reordering across 3 registers of different sizes"""
+        sim, real = self._get_backends(QE_TOKEN, QE_URL)
+        unittest.skipIf(not real, 'no remote device available.')
+
+        q0 = qiskit.QuantumRegister(2)
+        q1 = qiskit.QuantumRegister(2)
+        q2 = qiskit.QuantumRegister(1)
+        c0 = qiskit.ClassicalRegister(2)
+        c1 = qiskit.ClassicalRegister(2)
+        c2 = qiskit.ClassicalRegister(1)
+        circ = qiskit.QuantumCircuit(q0, q1, q2, c0, c1, c2)
+        circ.h(q0[0])
+        circ.cx(q0[0], q2[0])
+        circ.x(q1[1])
+        circ.h(q2[0])
+        circ.ccx(q2[0], q1[1], q1[0])
+        circ.barrier()
+        circ.measure(q0[0], c2[0])
+        circ.measure(q0[1], c0[1])
+        circ.measure(q1[0], c0[0])
+        circ.measure(q1[1], c1[0])
+        circ.measure(q2[0], c1[1])
+
+        shots = 4000
+        result_real = execute(circ, real, {"shots": shots}, timeout=600)
+        result_sim = execute(circ, sim, {"shots": shots})
+        counts_real = result_real.get_counts()
+        counts_sim = result_sim.get_counts()
+        threshold = 0.2 * shots
+        self.assertDictAlmostEqual(counts_real, counts_sim, threshold)
+
+    def _get_backends(self, QE_TOKEN, QE_URL):
+        sim_backend = 'local_qasm_simulator'
+        try:
+            register(QE_TOKEN, QE_URL)
+            real_backends = available_backends({'simulator': False})
+            real_backend = lowest_pending_jobs(real_backends)
+        except Exception:
+            real_backend = None
+
+        return sim_backend, real_backend
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)