Repository: dwavesystems/dimod

Issue:
BinaryQuadraticModel should have a correct abstract base class
Should be `collections.abc.Sized` as currently implemented.
Also could be `collections.abc.Container` or even `collections.abc.Collection`.

Discussion:

Patch:
diff --git a/dimod/binary_quadratic_model.py b/dimod/binary_quadratic_model.py
index 47a6c83b..80397f47 100644
--- a/dimod/binary_quadratic_model.py
+++ b/dimod/binary_quadratic_model.py
@@ -5,6 +5,7 @@ todo - describe Ising, QUBO and BQM
 """
 from __future__ import absolute_import, division
 
+from collections import Sized, Container, Iterable
 from numbers import Number
 
 from six import itervalues, iteritems, iterkeys
@@ -15,7 +16,7 @@ from dimod.utilities import resolve_label_conflict
 from dimod.vartypes import Vartype
 
 
-class BinaryQuadraticModel(object):
+class BinaryQuadraticModel(Sized, Container, Iterable):
     """Encodes a binary quadratic model.
 
     Binary quadratic model is the superclass that contains the `Ising model`_ and the QUBO_.
@@ -60,12 +61,23 @@ class BinaryQuadraticModel(object):
         class assume that they are numeric.
 
     Examples:
-        This example creates a model with three spin variables.
+        This example creates a binary quadratic model with three spin variables.
 
-        >>> model = dimod.BinaryQuadraticModel({0: 1, 1: -1, 2: .5},
-        ...                                    {(0, 1): .5, (1, 2): 1.5},
-        ...                                    1.4,
-        ...                                    dimod.SPIN)
+        >>> bqm = dimod.BinaryQuadraticModel({0: 1, 1: -1, 2: .5},
+        ...                                  {(0, 1): .5, (1, 2): 1.5},
+        ...                                  1.4,
+        ...                                  dimod.SPIN)
+
+        Variables can be any hashable object
+
+        >>> bqm = dimod.BinaryQuadraticModel({'a': 0.0, 'b': -1.0, 'c': 0.5},
+        ...                                  {('a', 'b'): -1.0, ('b', 'c'): 1.5},
+        ...                                  1.4,
+        ...                                  dimod.SPIN)
+        >>> len(bqm)
+        3
+        >>> 'b' in bqm
+        True
 
     Attributes:
         linear (dict[variable, bias]):
@@ -195,7 +207,14 @@ class BinaryQuadraticModel(object):
 
     def __len__(self):
         """The length is number of variables."""
-        return len(self.linear)
+        return self.adj.__len__()
+
+    def __contains__(self, v):
+        """The variables"""
+        return self.adj.__contains__(v)
+
+    def __iter__(self):
+        return self.adj.__iter__()
 
 ##################################################################################################
 # vartype properties
diff --git a/dimod/embedding/transforms.py b/dimod/embedding/transforms.py
index 332e0ec2..2b2d3b52 100644
--- a/dimod/embedding/transforms.py
+++ b/dimod/embedding/transforms.py
@@ -386,8 +386,11 @@ def unembed_response(target_response, embedding, source_bqm, chain_break_method=
         chain_break_method (function, optional, default=:func:`.majority_vote`):
             The method used to resolve chain breaks.
 
+    Returns:
+        :obj:`.Response`
+
     """
-    if any(v not in source_bqm.linear for v in embedding):
+    if any(v not in embedding for v in source_bqm):
         raise ValueError("given bqm does not match the embedding")
 
     energies = []


Test patch:
diff --git a/tests/test_binary_quadratic_model.py b/tests/test_binary_quadratic_model.py
index b8593fa0..72f9793d 100644
--- a/tests/test_binary_quadratic_model.py
+++ b/tests/test_binary_quadratic_model.py
@@ -191,6 +191,25 @@ class TestBinaryQuadraticModel(unittest.TestCase):
 
         self.assertEqual(len(bqm), len(linear))
 
+    def test__contains__(self):
+        bqm = dimod.BinaryQuadraticModel({'a': -1}, {}, 0.0, dimod.SPIN)
+
+        self.assertIn('a', bqm)
+        self.assertNotIn('b', bqm)
+
+        bqm.add_interaction('a', 'b', .5)
+
+        self.assertIn('b', bqm)
+
+    def test__iter__(self):
+        bqm = dimod.BinaryQuadraticModel.empty(dimod.BINARY)
+
+        self.assertEqual(set(bqm), set())
+
+        bqm.add_interaction('a', 'b', -1)
+
+        self.assertEqual(set(bqm), {'a', 'b'})
+
     def test_add_variable(self):
         bqm = dimod.BinaryQuadraticModel({}, {('a', 'b'): -1}, 0.0, dimod.SPIN)
         bqm.add_variable('a', .5)