Repository: dwavesystems/dwave-cloud-client

Issue:
computation.occurrences() output not as expected
I set QPU parameter answer_mode=histogram and submit a problem with several ground states, I expected to see the result returned in a histogram with ~4 occurrences buckets, similar to what results through SAPI:
![image](https://user-images.githubusercontent.com/34041130/39498504-5df90e42-4d5d-11e8-85fb-bad595f0ae64.png)

The resultant samples have repetitions (e.g. 3rd and 4th) but occurrences are always 1:

```
In [690]: with Client.from_config() as client:
     ...:   solver = client.get_solver()
     ...:   solver.parameters['answer_mode']='histogram'
     ...:   quad = {(16, 20): -1, (17, 20): 1, (16, 21): 1, (17, 21): 1}
     ...:   computation = solver.sample_ising({}, quad, num_reads=20)
     ...:   for i in range(len(computation.occurrences)):
     ...:       print(computation.samples[i][16],computation.samples[i][17], computation.samples[i][20], computation.sa
     ...: mples[i][21], ' --> ', computation.energies[i], computation.occurrences[i])
     ...:
(-1, 1, -1, 1, ' --> ', -2.0, 1)
(-1, -1, -1, 1, ' --> ', -2.0, 1)
(1, -1, 1, -1, ' --> ', -2.0, 1)
(1, -1, 1, -1, ' --> ', -2.0, 1)
(1, -1, 1, -1, ' --> ', -2.0, 1)
(1, -1, 1, 1, ' --> ', -2.0, 1)
(-1, 1, -1, -1, ' --> ', -2.0, 1)
(1, -1, 1, 1, ' --> ', -2.0, 1)
(1, -1, 1, 1, ' --> ', -2.0, 1)
(-1, -1, 1, 1, ' --> ', -2.0, 1)
(1, -1, 1, -1, ' --> ', -2.0, 1)
(-1, 1, -1, -1, ' --> ', -2.0, 1)
(1, 1, -1, -1, ' --> ', -2.0, 1)
(-1, -1, -1, 1, ' --> ', -2.0, 1)
(-1, 1, -1, 1, ' --> ', -2.0, 1)
(-1, -1, 1, 1, ' --> ', -2.0, 1)
(1, 1, -1, -1, ' --> ', -2.0, 1)
(1, 1, 1, -1, ' --> ', -2.0, 1)
(1, 1, 1, -1, ' --> ', -2.0, 1)
(1, -1, 1, 1, ' --> ', -2.0, 1)

```

Comments:

Patch:
diff --git a/dwave/cloud/__init__.py b/dwave/cloud/__init__.py
index f46c8ba..15e0bed 100644
--- a/dwave/cloud/__init__.py
+++ b/dwave/cloud/__init__.py
@@ -17,3 +17,14 @@ handler.setFormatter(formatter)
 root = logging.getLogger(__name__)
 root.setLevel(logging.WARNING)
 root.addHandler(handler)
+
+
+# add TRACE log level and Logger.trace() method
+TRACE_LOGLEVEL = 5
+
+logging.addLevelName(TRACE_LOGLEVEL, "TRACE")
+def _trace(logger, message, *args, **kws):
+    if logger.isEnabledFor(TRACE_LOGLEVEL):
+        logger._log(TRACE_LOGLEVEL, message, args, **kws)
+
+logging.Logger.trace = _trace
diff --git a/dwave/cloud/client.py b/dwave/cloud/client.py
index 89b5146..bf283fe 100644
--- a/dwave/cloud/client.py
+++ b/dwave/cloud/client.py
@@ -751,6 +751,7 @@ class Client(object):
         try:
             status = message['status']
             _LOGGER.debug("Handling response for %s with status %s", message['id'], status)
+            _LOGGER.trace("Handling response: %r", message)
 
             # The future may not have the ID set yet
             with future._single_cancel_lock:
diff --git a/dwave/cloud/coders.py b/dwave/cloud/coders.py
index 8b91335..43fc314 100644
--- a/dwave/cloud/coders.py
+++ b/dwave/cloud/coders.py
@@ -3,7 +3,8 @@ from __future__ import division, absolute_import
 import struct
 import base64
 
-from dwave.cloud.utils import uniform_iterator, uniform_get
+from dwave.cloud.utils import (
+    uniform_iterator, uniform_get, strip_tail, active_qubits)
 
 __all__ = ['encode_bqm_as_qp', 'decode_qp', 'decode_qp_numpy']
 
@@ -25,20 +26,25 @@ def encode_bqm_as_qp(solver, linear, quadratic):
     Returns:
         encoded submission dictionary
     """
+    active = active_qubits(linear, quadratic)
 
     # Encode linear terms. The coefficients of the linear terms of the objective
     # are encoded as an array of little endian 64 bit doubles.
     # This array is then base64 encoded into a string safe for json.
     # The order of the terms is determined by the _encoding_qubits property
     # specified by the server.
-    lin = [uniform_get(linear, qubit, 0) for qubit in solver._encoding_qubits]
+    # Note: only active qubits are coded with double, inactive with NaN
+    nan = float('nan')
+    lin = [uniform_get(linear, qubit, 0 if qubit in active else nan)
+           for qubit in solver._encoding_qubits]
     lin = base64.b64encode(struct.pack('<' + ('d' * len(lin)), *lin))
 
     # Encode the coefficients of the quadratic terms of the objective
     # in the same manner as the linear terms, in the order given by the
-    # _encoding_couplers property
-    quad = [quadratic.get(edge, 0) + quadratic.get((edge[1], edge[0]), 0)
-            for edge in solver._encoding_couplers]
+    # _encoding_couplers property, discarding tailing zero couplings
+    quad = [quadratic.get((q1,q2), 0) + quadratic.get((q2,q1), 0)
+            for (q1,q2) in solver._encoding_couplers
+            if q1 in active and q2 in active]
     quad = base64.b64encode(struct.pack('<' + ('d' * len(quad)), *quad))
 
     # The name for this encoding is 'qp' and is explicitly included in the
@@ -192,8 +198,9 @@ def decode_qp_numpy(msg, return_matrix=True):
                          dtype=byte_type))
 
     # Clip off the extra bits from encoding
-    bits = np.reshape(bits, (num_solutions, bits.size // num_solutions))
-    bits = np.delete(bits, range(num_variables, bits.shape[1]), 1)
+    if num_solutions:
+        bits = np.reshape(bits, (num_solutions, bits.size // num_solutions))
+        bits = np.delete(bits, range(num_variables, bits.shape[1]), 1)
 
     # Switch from bits to spins
     default = 3
diff --git a/dwave/cloud/solver.py b/dwave/cloud/solver.py
index 2a2cf4a..094ac8a 100644
--- a/dwave/cloud/solver.py
+++ b/dwave/cloud/solver.py
@@ -241,6 +241,7 @@ class Solver(object):
             'type': type_,
             'params': params
         })
+        _LOGGER.trace("Encoded sample request: %s", body)
 
         future = Future(solver=self, id_=None, return_matrix=self.return_matrix,
                         submission_data=(type_, linear, quadratic, params))
@@ -278,8 +279,6 @@ class Solver(object):
             ...
             False
             True
-
-
         """
         for key, value in uniform_iterator(linear):
             if value != 0 and key not in self.nodes:
diff --git a/dwave/cloud/utils.py b/dwave/cloud/utils.py
index 0294da8..6d67e0b 100644
--- a/dwave/cloud/utils.py
+++ b/dwave/cloud/utils.py
@@ -3,6 +3,7 @@ from __future__ import division, absolute_import
 from datetime import datetime
 from dateutil.tz import UTC
 from functools import wraps
+import itertools
 
 import six
 import readline
@@ -29,6 +30,7 @@ def evaluate_ising(linear, quad, state):
     Returns:
         Energy of the state evaluated by the given energy function.
     """
+
     # If we were given a numpy array cast to list
     if _numpy and isinstance(state, np.ndarray):
         return evaluate_ising(linear, quad, state.tolist())
@@ -42,6 +44,16 @@ def evaluate_ising(linear, quad, state):
     return energy
 
 
+def active_qubits(linear, quadratic):
+    """Calculate a set of all active qubits. Qubit is "active" if it has
+    bias or coupling attached."""
+
+    active = set(linear)
+    for edge, _ in six.iteritems(quadratic):
+        active.update(edge)
+    return active
+
+
 def uniform_iterator(sequence):
     """Uniform (key, value) iteration on a `dict`,
     or (idx, value) on a `list`."""
@@ -62,6 +74,17 @@ def uniform_get(sequence, index, default=None):
         return sequence[index] if index < len(sequence) else default
 
 
+def strip_head(sequence, values):
+    """Strips elements of `values` from the beginning of `sequence`."""
+    values = set(values)
+    return list(itertools.dropwhile(lambda x: x in values, sequence))
+
+
+def strip_tail(sequence, values):
+    """Strip `values` from the end of `sequence`."""
+    return list(reversed(list(strip_head(reversed(sequence), values))))
+
+
 def readline_input(prompt, prefill=''):
     """Provide an editable default for ``input()``."""
     # see: https://stackoverflow.com/q/2533120/


Test patch:
diff --git a/tests/test_coders.py b/tests/test_coders.py
index 5c90433..382796b 100644
--- a/tests/test_coders.py
+++ b/tests/test_coders.py
@@ -24,7 +24,9 @@ def get_solver():
 
 class TestCoders(unittest.TestCase):
 
-    def test_qpu_request_encoding(self):
+    def test_qpu_request_encoding_all_qubits(self):
+        """Test biases and coupling strengths are properly encoded (base64 little-endian doubles)."""
+
         solver = get_solver()
         linear = {index: 1 for index in solver.nodes}
         quadratic = {key: -1 for key in solver.undirected_edges}
@@ -32,3 +34,55 @@ class TestCoders(unittest.TestCase):
         self.assertEqual(request['format'], 'qp')
         self.assertEqual(request['lin'],  'AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/AAAAAAAA8D8=')
         self.assertEqual(request['quad'], 'AAAAAAAA8L8AAAAAAADwvwAAAAAAAPC/AAAAAAAA8L8=')
+
+    def test_qpu_request_encoding_sub_qubits(self):
+        """Inactive qubits should be encoded as NaNs. Inactive couplers should be omitted."""
+
+        solver = get_solver()
+        linear = {index: 1 for index in sorted(list(solver.nodes))[:2]}
+        quadratic = {key: -1 for key in sorted(list(solver.undirected_edges))[:1]}
+        request = encode_bqm_as_qp(solver, linear, quadratic)
+        self.assertEqual(request['format'], 'qp')
+        # [1, 1, NaN, NaN]
+        self.assertEqual(request['lin'],  'AAAAAAAA8D8AAAAAAADwPwAAAAAAAPh/AAAAAAAA+H8=')
+        # [-1]
+        self.assertEqual(request['quad'], 'AAAAAAAA8L8=')
+
+    def test_qpu_request_encoding_missing_qubits(self):
+        """Qubits don't have to be specified with biases only, but also with couplings."""
+
+        solver = get_solver()
+        linear = {}
+        quadratic = {(0, 1): -1}
+        request = encode_bqm_as_qp(solver, linear, quadratic)
+        self.assertEqual(request['format'], 'qp')
+        # [0, 0, NaN, NaN]
+        self.assertEqual(request['lin'],  'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPh/AAAAAAAA+H8=')
+        # [-1]
+        self.assertEqual(request['quad'], 'AAAAAAAA8L8=')
+
+    def test_qpu_request_encoding_sub_qubits_implicit_biases(self):
+        """Biases don't have to be specified for qubits to be active."""
+
+        solver = get_solver()
+        linear = {}
+        quadratic = {(0,3): -1}
+        request = encode_bqm_as_qp(solver, linear, quadratic)
+        self.assertEqual(request['format'], 'qp')
+        # [0, NaN, NaN, 0]
+        self.assertEqual(request['lin'],  'AAAAAAAAAAAAAAAAAAD4fwAAAAAAAPh/AAAAAAAAAAA=')
+        # [-1]
+        self.assertEqual(request['quad'], 'AAAAAAAA8L8=')
+
+    def test_qpu_request_encoding_sub_qubits_implicit_couplings(self):
+        """Couplings should be zero for active qubits, if not specified."""
+
+        solver = get_solver()
+        linear = {0: 0, 3: 0}
+        quadratic = {}
+        request = encode_bqm_as_qp(solver, linear, quadratic)
+        self.assertEqual(request['format'], 'qp')
+        # [0, NaN, NaN, 0]
+        self.assertEqual(request['lin'],  'AAAAAAAAAAAAAAAAAAD4fwAAAAAAAPh/AAAAAAAAAAA=')
+        # [-1]
+        self.assertEqual(request['quad'], 'AAAAAAAAAAA=')
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a3fcd6f..8a6f4db 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,7 +1,8 @@
 import unittest
 from collections import OrderedDict
 
-from dwave.cloud.utils import readline_input, uniform_iterator, uniform_get
+from dwave.cloud.utils import (
+    readline_input, uniform_iterator, uniform_get, strip_head, strip_tail)
 from dwave.cloud.testing import mock
 
 
@@ -22,6 +23,18 @@ class TestUtils(unittest.TestCase):
         self.assertEqual(uniform_get(l, 2), None)
         self.assertEqual(uniform_get(l, 2, default=0), 0)
 
+    def test_strip_head(self):
+        self.assertEqual(strip_head([0, 0, 1, 2], [0]), [1, 2])
+        self.assertEqual(strip_head([1], [0]), [1])
+        self.assertEqual(strip_head([1], []), [1])
+        self.assertEqual(strip_head([0, 0, 1, 2], [0, 1, 2]), [])
+
+    def test_strip_tail(self):
+        self.assertEqual(strip_tail([1, 2, 0, 0], [0]), [1, 2])
+        self.assertEqual(strip_tail([1], [0]), [1])
+        self.assertEqual(strip_tail([1], []), [1])
+        self.assertEqual(strip_tail([0, 0, 1, 2], [0, 1, 2]), [])
+
     def test_readline_input(self):
         val = "value"
         with mock.patch("six.moves.input", side_effect=[val], create=True):