Skip to content

Commit

Permalink
Remove direct dependency on decorators package
Browse files Browse the repository at this point in the history
Closes #212
  • Loading branch information
arcondello committed Nov 26, 2021
1 parent 34ae89a commit 3f73497
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 116 deletions.
154 changes: 40 additions & 114 deletions dwave_networkx/utils/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
algorithms.
"""

from decorator import decorator
import functools
import inspect

import dwave_networkx as dnx

Expand All @@ -34,117 +35,42 @@ def binary_quadratic_model_sampler(which_args):
`function_name(args, *kw)`. If more than one
sampler is allowed, can be a list of locations.
Returns
-------
_binary_quadratic_model_sampler : function
Caller function that validates the sampler format. A sampler
is expected to have `sample_qubo` and `sample_ising` methods.
Alternatively, if no sampler is provided (or sampler is None),
the sampler set by the `set_default_sampler` function is provided to
the function.
Examples
--------
Decorate functions like this::
@binary_quadratic_model_sampler(1)
def maximal_matching(G, sampler, **sampler_args):
pass
This example validates two placeholder samplers, which return a correct
response only in the case of finding an independent set on a complete graph
(where one node is always an independent set), the first valid, the second
missing a method.
>>> import networkx as nx
>>> import dwave_networkx as dnx
>>> from dwave_networkx.utils import decorators
>>> # Create two placeholder samplers
>>> class WellDefinedSampler:
... # an example sampler, only works for independent set on complete
... # graphs
... def __init__(self, name):
... self.name = name
... def sample_ising(self, h, J):
... sample = {v: -1 for v in h}
... sample[0] = 1 # set one node to true
... return [sample]
... def sample_qubo(self, Q):
... sample = {v: 0 for v in set().union(*Q)}
... sample[0] = 1 # set one node to true
... return [sample]
... def __str__(self):
... return self.name
...
>>> class IllDefinedSampler:
... # an example sampler missing a `sample_qubo` method
... def __init__(self, name):
... self.name = name
... def sample_ising(self, h, J):
... sample = {v: -1 for v in h}
... sample[0] = 1 # set one node to true
... return [sample]
... def __str__(self):
... return self.name
...
>>> sampler1 = WellDefinedSampler('sampler1')
>>> sampler2 = IllDefinedSampler('sampler2')
>>> # Define a placeholder independent-set function with the decorator
>>> @dnx.utils.binary_quadratic_model_sampler(1)
... def independent_set(G, sampler, **sampler_args):
... Q = {(node, node): -1 for node in G}
... Q.update({edge: 2 for edge in G.edges})
... response = sampler.sample_qubo(Q, **sampler_args)
... sample = next(iter(response))
... return [node for node in sample if sample[node] > 0]
...
>>> # Validate the samplers
>>> G = nx.complete_graph(5)
>>> independent_set(G, sampler1)
[0]
>>> independent_set(G, sampler2) # doctest: +SKIP
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-35-670b71b268c7> in <module>()
----> 1 independent_set(G, IllDefinedSampler)
<decorator-gen-628> in independent_set(G, sampler, **sampler_args)
/usr/local/lib/python2.7/dist-packages/dwave_networkx/utils/decorators.pyc in _binary_quadratic_model_sampler(f, *args, **kw)
61
62 if not hasattr(sampler, "sample_qubo") or not callable(sampler.sample_qubo):
---> 63 raise TypeError("expected sampler to have a 'sample_qubo' method")
64 if not hasattr(sampler, "sample_ising") or not callable(sampler.sample_ising):
65 raise TypeError("expected sampler to have a 'sample_ising' method")
TypeError: expected sampler to have a 'sample_qubo' method
"""
@decorator
def _binary_quadratic_model_sampler(f, *args, **kw):
# convert into a sequence if necessary
if isinstance(which_args, int):
iter_args = (which_args,)
else:
iter_args = iter(which_args)

# check each sampler for the correct methods
new_args = [arg for arg in args]
for idx in iter_args:
sampler = args[idx]

# if no sampler is provided, get the default sampler if it has
# been set
if sampler is None:
# this sampler has already been vetted
default_sampler = dnx.get_default_sampler()
if default_sampler is None:
raise dnx.DWaveNetworkXMissingSampler('no default sampler set')
new_args[idx] = default_sampler
continue

if not hasattr(sampler, "sample_qubo") or not callable(sampler.sample_qubo):
raise TypeError("expected sampler to have a 'sample_qubo' method")
if not hasattr(sampler, "sample_ising") or not callable(sampler.sample_ising):
raise TypeError("expected sampler to have a 'sample_ising' method")

# now run the function and return the results
return f(*new_args, **kw)
return _binary_quadratic_model_sampler
def decorator(f):
@functools.wraps(f)
def func(*args, **kwargs):
bound_arguments = inspect.signature(f).bind(*args, **kwargs)
bound_arguments.apply_defaults()

args = bound_arguments.args
kw = bound_arguments.kwargs

if isinstance(which_args, int):
iter_args = (which_args,)
else:
iter_args = iter(which_args)

# check each sampler for the correct methods
new_args = [arg for arg in args]
for idx in iter_args:
sampler = args[idx]

# if no sampler is provided, get the default sampler if it has
# been set
if sampler is None:
# this sampler has already been vetted
default_sampler = dnx.get_default_sampler()
if default_sampler is None:
raise dnx.DWaveNetworkXMissingSampler('no default sampler set')
new_args[idx] = default_sampler
continue

if not hasattr(sampler, "sample_qubo") or not callable(sampler.sample_qubo):
raise TypeError("expected sampler to have a 'sample_qubo' method")
if not hasattr(sampler, "sample_ising") or not callable(sampler.sample_ising):
raise TypeError("expected sampler to have a 'sample_ising' method")

# now run the function and return the results
return f(*new_args, **kw)
return func
return decorator
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
networkx==2.4
decorator==4.3.0
dimod==0.8.0
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
]

install_requires = ['networkx>=2.0,<3.0',
'decorator>=4.1.0,<5.0.0',
'dimod>=0.8.0,!=0.10.0,!=0.10.1,!=0.10.2,!=0.10.3,!=0.10.4',
]

Expand Down

0 comments on commit 3f73497

Please sign in to comment.