|
61 | 61 | [0]
|
62 | 62 |
|
63 | 63 | """
|
| 64 | +from decorator import decorator |
64 | 65 |
|
65 |
| -from dwave.plugins.networkx.utils.decorators import binary_quadratic_model_sampler |
66 |
| - |
67 |
| -__all__ = ['set_default_sampler', 'get_default_sampler', 'unset_default_sampler'] |
| 66 | +__all__ = ['set_default_sampler', |
| 67 | + 'get_default_sampler', |
| 68 | + 'unset_default_sampler', |
| 69 | + ] |
68 | 70 |
|
69 | 71 |
|
70 | 72 | _SAMPLER = None
|
71 | 73 |
|
72 | 74 |
|
| 75 | +def binary_quadratic_model_sampler(which_args): |
| 76 | + """Decorator to validate sampler arguments. |
| 77 | +
|
| 78 | + Parameters |
| 79 | + ---------- |
| 80 | + which_args : int or sequence of ints |
| 81 | + Location of the sampler arguments of the input function in the form |
| 82 | + `function_name(args, *kw)`. If more than one |
| 83 | + sampler is allowed, can be a list of locations. |
| 84 | +
|
| 85 | + Returns |
| 86 | + ------- |
| 87 | + _binary_quadratic_model_sampler : function |
| 88 | + Caller function that validates the sampler format. A sampler |
| 89 | + is expected to have `sample_qubo` and `sample_ising` methods. |
| 90 | + Alternatively, if no sampler is provided (or sampler is None), |
| 91 | + the sampler set by the `set_default_sampler` function is provided to |
| 92 | + the function. |
| 93 | +
|
| 94 | + Examples |
| 95 | + -------- |
| 96 | + Decorate functions like this:: |
| 97 | +
|
| 98 | + @binary_quadratic_model_sampler(1) |
| 99 | + def maximal_matching(G, sampler, **sampler_args): |
| 100 | + pass |
| 101 | +
|
| 102 | + This example validates two placeholder samplers, which return a correct |
| 103 | + response only in the case of finding an independent set on a complete graph |
| 104 | + (where one node is always an independent set), the first valid, the second |
| 105 | + missing a method. |
| 106 | +
|
| 107 | + >>> import networkx as nx |
| 108 | + >>> import dwave.plugins.networkx as dnx |
| 109 | + >>> from dwave.plugins.networkx.utils import decorators |
| 110 | + >>> # Create two placeholder samplers |
| 111 | + >>> class WellDefinedSampler: |
| 112 | + ... # an example sampler, only works for independent set on complete |
| 113 | + ... # graphs |
| 114 | + ... def __init__(self, name): |
| 115 | + ... self.name = name |
| 116 | + ... def sample_ising(self, h, J): |
| 117 | + ... sample = {v: -1 for v in h} |
| 118 | + ... sample[0] = 1 # set one node to true |
| 119 | + ... return [sample] |
| 120 | + ... def sample_qubo(self, Q): |
| 121 | + ... sample = {v: 0 for v in set().union(*Q)} |
| 122 | + ... sample[0] = 1 # set one node to true |
| 123 | + ... return [sample] |
| 124 | + ... def __str__(self): |
| 125 | + ... return self.name |
| 126 | + ... |
| 127 | + >>> class IllDefinedSampler: |
| 128 | + ... # an example sampler missing a `sample_qubo` method |
| 129 | + ... def __init__(self, name): |
| 130 | + ... self.name = name |
| 131 | + ... def sample_ising(self, h, J): |
| 132 | + ... sample = {v: -1 for v in h} |
| 133 | + ... sample[0] = 1 # set one node to true |
| 134 | + ... return [sample] |
| 135 | + ... def __str__(self): |
| 136 | + ... return self.name |
| 137 | + ... |
| 138 | + >>> sampler1 = WellDefinedSampler('sampler1') |
| 139 | + >>> sampler2 = IllDefinedSampler('sampler2') |
| 140 | + >>> # Define a placeholder independent-set function with the decorator |
| 141 | + >>> @dnx.utils.binary_quadratic_model_sampler(1) |
| 142 | + ... def independent_set(G, sampler, **sampler_args): |
| 143 | + ... Q = {(node, node): -1 for node in G} |
| 144 | + ... Q.update({edge: 2 for edge in G.edges}) |
| 145 | + ... response = sampler.sample_qubo(Q, **sampler_args) |
| 146 | + ... sample = next(iter(response)) |
| 147 | + ... return [node for node in sample if sample[node] > 0] |
| 148 | + ... |
| 149 | + >>> # Validate the samplers |
| 150 | + >>> G = nx.complete_graph(5) |
| 151 | + >>> independent_set(G, sampler1) |
| 152 | + [0] |
| 153 | + >>> independent_set(G, sampler2) # doctest: +SKIP |
| 154 | + --------------------------------------------------------------------------- |
| 155 | + TypeError Traceback (most recent call last) |
| 156 | + <ipython-input-35-670b71b268c7> in <module>() |
| 157 | + ----> 1 independent_set(G, IllDefinedSampler) |
| 158 | + <decorator-gen-628> in independent_set(G, sampler, **sampler_args) |
| 159 | + /usr/local/lib/python2.7/dist-packages/dwave.plugins.networkx/utils/decorators.pyc in _binary_quadratic_model_sampler(f, *args, **kw) |
| 160 | + 61 |
| 161 | + 62 if not hasattr(sampler, "sample_qubo") or not callable(sampler.sample_qubo): |
| 162 | + ---> 63 raise TypeError("expected sampler to have a 'sample_qubo' method") |
| 163 | + 64 if not hasattr(sampler, "sample_ising") or not callable(sampler.sample_ising): |
| 164 | + 65 raise TypeError("expected sampler to have a 'sample_ising' method") |
| 165 | + TypeError: expected sampler to have a 'sample_qubo' method |
| 166 | +
|
| 167 | + """ |
| 168 | + @decorator |
| 169 | + def _binary_quadratic_model_sampler(f, *args, **kw): |
| 170 | + # convert into a sequence if necessary |
| 171 | + if isinstance(which_args, int): |
| 172 | + iter_args = (which_args,) |
| 173 | + else: |
| 174 | + iter_args = iter(which_args) |
| 175 | + |
| 176 | + # check each sampler for the correct methods |
| 177 | + new_args = [arg for arg in args] |
| 178 | + for idx in iter_args: |
| 179 | + sampler = args[idx] |
| 180 | + |
| 181 | + # if no sampler is provided, get the default sampler if it has |
| 182 | + # been set |
| 183 | + if sampler is None: |
| 184 | + # this sampler has already been vetted |
| 185 | + default_sampler = get_default_sampler() |
| 186 | + if default_sampler is None: |
| 187 | + raise DWaveNetworkXMissingSampler('no default sampler set') |
| 188 | + new_args[idx] = default_sampler |
| 189 | + continue |
| 190 | + |
| 191 | + if not hasattr(sampler, "sample_qubo") or not callable(sampler.sample_qubo): |
| 192 | + raise TypeError("expected sampler to have a 'sample_qubo' method") |
| 193 | + if not hasattr(sampler, "sample_ising") or not callable(sampler.sample_ising): |
| 194 | + raise TypeError("expected sampler to have a 'sample_ising' method") |
| 195 | + |
| 196 | + # now run the function and return the results |
| 197 | + return f(*new_args, **kw) |
| 198 | + return _binary_quadratic_model_sampler |
| 199 | + |
| 200 | + |
73 | 201 | @binary_quadratic_model_sampler(0)
|
74 | 202 | def set_default_sampler(sampler):
|
75 | 203 | """Sets a default binary quadratic model sampler.
|
|
0 commit comments