def sample(self, bqm, num_reads=10): """Gives random samples. Args: todo Returns: :obj:`.Response`: The vartype will match the given binary quadratic model. Notes: For each variable in each sample, the value is chosen by a coin flip. """ values = np.asarray(list(bqm.vartype.value), dtype='int8') samples = np.random.choice(values, (num_reads, len(bqm))) df_samples = pd.DataFrame(samples, columns=bqm.linear) energies = [ bqm.energy(sample) for idx, sample in df_samples.iterrows() ] response = Response(bqm.vartype) response.add_samples_from(df_samples, energies) return response
def sample(self, bqm, beta_range=None, num_reads=10, num_sweeps=1000): """Sample from low-energy spin states using simulated annealing. Args: todo beta_range (tuple, optional): A 2-tuple defining the beginning and end of the beta schedule (beta is the inverse temperature). The schedule is applied linearly in beta. Default is chosen based on the total bias associated with each node. num_reads (int, optional): Each sample is the result of a single run of the simulated annealing algorithm. num_sweeps (int, optional): The number of sweeps or steps. Default is 1000. Returns: :obj:`Response` Examples: >>> sampler = SimulatedAnnealingSampler() >>> h = {0: -1, 1: -1} >>> J = {(0, 1): -1} >>> response = sampler.sample_ising(h, J, num_reads=1) >>> list(response.samples()) [{0: 1, 1: 1}] Note: This is a reference implementation, not optimized for speed and therefore not an appropriate sampler for benchmarking. """ # input checking # h, J are handled by the @ising decorator # beta_range, sweeps are handled by ising_simulated_annealing if not isinstance(num_reads, int): raise TypeError("'samples' should be a positive integer") if num_reads < 1: raise ValueError("'samples' should be a positive integer") # create the response object. Ising returns spin values. response = Response(Vartype.SPIN) h, J, offset = to_ising(bqm) # run the simulated annealing algorithm for __ in range(num_reads): sample, energy = ising_simulated_annealing(h, J, beta_range, num_sweeps) response.add_sample(sample, energy + offset) return response
def sample(self, bqm, num_reads=10): """Gives random samples. Args: todo Returns: :obj:`.Response`: The vartype will match the given binary quadratic model. Notes: For each variable in each sample, the value is chosen by a coin flip. """ values = np.asarray(list(bqm.vartype.value), dtype='int8') samples = np.random.choice(values, (num_reads, len(bqm))) variable_labels = list(bqm.linear) label_to_idx = {v: idx for idx, v in enumerate(variable_labels)} energies = [ bqm.energy(SampleView(idx, samples, label_to_idx)) for idx in range(num_reads) ] return Response.from_matrix(samples, {'energy': energies}, vartype=bqm.vartype, variable_labels=variable_labels)
def sample(self, bqm): M = bqm.binary.to_numpy_matrix() sample = np.zeros((len(bqm),), dtype=bool) # now we iterate, flipping one bit at a time until we have # traversed all samples. This is a Gray code. # https://en.wikipedia.org/wiki/Gray_code def iter_samples(): sample = np.zeros((len(bqm)), dtype=bool) energy = 0.0 yield sample.copy(), energy for i in range(1, 1 << len(bqm)): v = _ffs(i) # flip the bit in the sample sample[v] = not sample[v] # for now just calculate the energy, but there is a more clever way by calculating # the energy delta for the single bit flip, don't have time, pull requests # appreciated! energy = sample.dot(M).dot(sample.transpose()) yield sample.copy(), float(energy) + bqm.offset samples, energies = zip(*iter_samples()) response = Response.from_matrix(np.asarray(samples), {'energy': energies}) # finally make sure the response matches the given vartype, in-place. response.change_vartype(bqm.vartype) return response
def sample(self, bqm, num_reads=10): """Gives random samples. Args: todo Returns: :obj:`.Response`: The vartype will match the given binary quadratic model. Notes: For each variable in each sample, the value is chosen by a coin flip. """ values = tuple(bqm.vartype.value) def _itersample(): for __ in range(num_reads): sample = {v: choice(values) for v in bqm.linear} energy = bqm.energy(sample) yield sample, energy samples, energies = zip(*_itersample()) return Response.from_dicts(samples, {'energy': energies}, vartype=bqm.vartype)
def sample(self, bqm, beta_range=None, num_reads=10, num_sweeps=1000): """Sample from low-energy spin states using simulated annealing. Args: bqm (:obj:`~dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. beta_range (tuple, optional): Beginning and end of the beta schedule (beta is the inverse temperature) as a 2-tuple. The schedule is applied linearly in beta. Default is chosen based on the total bias associated with each node. num_reads (int, optional): Number of reads. Each sample is the result of a single run of the simulated annealing algorithm. num_sweeps (int, optional): Number of sweeps or steps. Default is 1000. Returns: :obj:`~dimod.Response`: A `dimod` :obj:`.~dimod.Response` object. Note: This is a reference implementation, not optimized for speed and therefore not an appropriate sampler for benchmarking. Examples: This example provides samples for a two-variable QUBO model. >>> import dimod ... >>> sampler = dimod.SimulatedAnnealingSampler() >>> Q = {(0, 0): -1, (1, 1): -1, (0, 1): 2} >>> bqm = dimod.BinaryQuadraticModel.from_qubo(Q, offset = 0.0) >>> response = sampler.sample(bqm, num_reads=2) >>> response.data_vectors['energy'] # doctest: +SKIP array([-1., -1.]) """ # input checking # h, J are handled by the @ising decorator # beta_range, sweeps are handled by ising_simulated_annealing if not isinstance(num_reads, int): raise TypeError("'samples' should be a positive integer") if num_reads < 1: raise ValueError("'samples' should be a positive integer") h, J, offset = bqm.to_ising() # run the simulated annealing algorithm samples = [] energies = [] for __ in range(num_reads): sample, energy = ising_simulated_annealing(h, J, beta_range, num_sweeps) samples.append(sample) energies.append(energy) response = Response.from_samples(samples, {'energy': energies}, {}, vartype=Vartype.SPIN) response.change_vartype(bqm.vartype, offset, inplace=True) return response
def sample(self, bqm, num_spin_reversal_transforms=2, spin_reversal_variables=None, **kwargs): # make a main response response = Response(bqm.vartype) for __ in range(num_spin_reversal_transforms): if spin_reversal_variables is None: # apply spin transform to each variable with 50% chance transform = list(v for v in bqm.linear if random() > .5) else: transform = list(spin_reversal_variables) flipped_bqm = bqm.copy() for v in transform: flipped_bqm.flip_variable(v) flipped_response = self.child.sample(bqm, **kwargs) data_kwargs = flipped_response.df_data.to_dict('list') if 'spin_reversal_variables' in data_kwargs: data_kwargs['spin_reversal_variables_{}'.format( time.time())] = [transform] * len(flipped_response) else: data_kwargs['spin_reversal_variables'] = [ transform ] * len(flipped_response) if bqm.vartype is Vartype.SPIN: flipped_response.df_samples[ transform] = -1 * flipped_response.df_samples[transform] else: flipped_response.df_samples[ transform] = 1 - flipped_response.df_samples[transform] response.add_samples_from(flipped_response.df_samples, **data_kwargs) return response
def sample(self, bqm, num_reads=10): """Give random samples for a binary quadratic model. Args: bqm (:obj:`~dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. num_reads (int, optional): Number of reads. Returns: :obj:`~dimod.Response`: A `dimod` :obj:`.~dimod.Response` object. Notes: For each variable in each sample, the value is chosen by a coin flip. Examples: This example provides samples for a two-variable Ising model. >>> import dimod ... >>> sampler = dimod.RandomSampler() >>> h = {0: -1, 1: -1} >>> J = {(0, 1): -1} >>> bqm = dimod.BinaryQuadraticModel(h, J, -0.5, dimod.SPIN) >>> response = sampler.sample(bqm, num_reads=3) >>> len(response) 3 >>> response.data_vectors['energy'] # doctest: +SKIP array([ 0.5, -3.5, 0.5]) """ values = tuple(bqm.vartype.value) def _itersample(): for __ in range(num_reads): sample = {v: choice(values) for v in bqm.linear} energy = bqm.energy(sample) yield sample, energy samples, energies = zip(*_itersample()) return Response.from_samples(samples, {'energy': energies}, {}, vartype=bqm.vartype)
def sample(self, bqm, num_spin_reversal_transforms=2, spin_reversal_variables=None, **kwargs): """Sample from the binary quadratic model. Args: bqm (:obj:`~dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. num_spin_reversal_transforms (integer, optional, default=2): Number of spin reversal transform runs. spin_reversal_variables (list/dict, optional, default=None): Variables to which to apply the spin reversal. If None, every variable has a 50% probability of being selected. Returns: :obj:`~dimod.Response`: A `dimod` :obj:`.~dimod.Response` object. Examples: This example runs 100 spin reversals applied to one variable of a QUBO problem. >>> import dimod >>> base_sampler = dimod.ExactSolver() >>> composed_sampler = dimod.SpinReversalTransformComposite(base_sampler) >>> Q = {('a', 'a'): -1, ('b', 'b'): -1, ('a', 'b'): 2} >>> response = composed_sampler.sample_qubo(Q, ... num_spin_reversal_transforms=100, ... spin_reversal_variables={'a'}) >>> len(response) 400 >>> print(next(response.data())) # doctest: +SKIP Sample(sample={'a': 0, 'b': 1}, energy=-1.0) """ # make a main response responses = [] for ii in range(num_spin_reversal_transforms): if spin_reversal_variables is None: # apply spin transform to each variable with 50% chance transform = list(v for v in bqm.linear if random() > .5) else: transform = list(spin_reversal_variables) flipped_bqm = bqm.copy() for v in transform: flipped_bqm.flip_variable(v) flipped_response = self.child.sample(bqm, **kwargs) tf_idxs = [ flipped_response.label_to_idx[v] for v in flipped_response.variable_labels ] if bqm.vartype is Vartype.SPIN: flipped_response.record.sample[:, tf_idxs] = -1 * flipped_response.record.sample[:, tf_idxs] else: flipped_response.record.sample[:, tf_idxs] = 1 - flipped_response.record.sample[:, tf_idxs] responses.append(flipped_response) # # stack the records record = np.rec.array(np.hstack((resp.record for resp in responses))) vartypes = set(resp.vartype for resp in responses) if len(vartypes) > 1: raise RuntimeError("inconsistent vartypes returned") vartype = vartypes.pop() info = {} for resp in responses: info.update(resp.info) labels = responses[0].variable_labels return Response(record, labels, info, vartype)
def unembed_response(target_response, embedding, source_bqm, chain_break_method=None): """Unembed the response. Construct a response for the source binary quadratic model (BQM) by unembedding the given response from the target BQM. Args: target_response (:obj:`.Response`): Response from the target BQM. embedding (dict): Mapping from source graph to target graph as a dict of form {s: {t, ...}, ...}, where s is a source-model variable and t is a target-model variable. source_bqm (:obj:`.BinaryQuadraticModel`): Source binary quadratic model. chain_break_method (function, optional): Method used to resolve chain breaks. Must be an iterator which accepts a sample and an embedding and yields unembedded samples. Returns: :obj:`.Response`: Response for the source binary quadratic model. Examples: This example embeds a Boolean AND gate, :math:`x_3 \Leftrightarrow x_1 \wedge x_2`, in a square-structured graph and samples with dimod reference structured sampler, `StructureComposite`, using the dimod reference `ExactSolver` sampler. The gate is represented as penalty model :math:`x_1 x_2 - 2(x_1+x_2)x_3 +3x_3`, which is submitted to the sampler as QUBO problem :math:`E(a_i, b_{i,j}; x_i) = 3x_3 + x_1x_2 - 2x_1x_3 - 2x_2x_3`. This QUBO represents a fully connected :math:`K_3` graph. Samples are unembedded by :func:`.unembed_response` and show that only valid states of the AND gate have zero energy (e.g., only input :math:`x_1 x_2=1,1` results in :math:`z=1`), while invalid states have higher energy. >>> import dimod >>> import networkx as nx >>> # Binary quadratic model for the AND gate >>> Q = {('x1', 'x2'): 1, ('x1', 'z'): -2, ('x2', 'z'): -2, ('z', 'z'): 3} >>> bqm = dimod.BinaryQuadraticModel.from_qubo(Q) >>> # Embed the BQM in a structured dimod sampler defined by a square graph >>> target_graph = nx.cycle_graph(4) >>> sampler = dimod.StructureComposite(dimod.ExactSolver(), ... list(target_graph.nodes), list(target_graph.edges)) >>> embedding = {'x1': {0}, 'x2': {1}, 'z': {2, 3}} >>> target_Q = dimod.embed_qubo(Q, embedding, sampler.adjacency) >>> # Sample on the target graph >>> target_response = sampler.sample_qubo(target_Q) >>> # Unembed samples back to the problem graph >>> source_response = dimod.unembed_response(target_response, embedding, bqm) >>> # Verify correct representation of the AND gate (first automatically then manually) >>> for datum in source_response.data(): ... if (datum.sample['x1'] and datum.sample['x2']) == datum.sample['z']: ... if datum.energy > 0: ... print('Valid AND has high energy') ... ... else: ... if datum.energy == 0: ... print('invalid AND has low energy') ... >>> for datum in source_response.data(): # doctest: +SKIP ... print(datum) ... Sample(sample={'x2': 0, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 0, 'x1': 1, 'z': 0}, energy=0.0) Sample(sample={'x2': 1, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 1, 'x1': 1, 'z': 1}, energy=0.0) Sample(sample={'x2': 1, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 0, 'x1': 1, 'z': 0}, energy=0.0) Sample(sample={'x2': 0, 'x1': 1, 'z': 0}, energy=0.0) Sample(sample={'x2': 0, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 1, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 0, 'x1': 0, 'z': 0}, energy=0.0) Sample(sample={'x2': 1, 'x1': 1, 'z': 0}, energy=1.0) Sample(sample={'x2': 0, 'x1': 1, 'z': 1}, energy=1.0) Sample(sample={'x2': 1, 'x1': 0, 'z': 1}, energy=1.0) Sample(sample={'x2': 1, 'x1': 1, 'z': 0}, energy=1.0) Sample(sample={'x2': 1, 'x1': 1, 'z': 0}, energy=1.0) Sample(sample={'x2': 0, 'x1': 0, 'z': 1}, energy=3.0) """ if any(v not in embedding for v in source_bqm): raise ValueError("given bqm does not match the embedding") if chain_break_method is None: chain_break_method = majority_vote variables, chains = zip(*embedding.items()) if target_response.label_to_idx is None: chain_idxs = [list(chain) for chain in chains] else: chain_idxs = [[target_response.label_to_idx[v] for v in chain] for chain in chains] record = target_response.record unembedded, idxs = chain_break_method(record.sample, chain_idxs) lin, (i, j, quad), off = source_bqm.to_numpy_vectors(variable_order=variables) energies = unembedded.dot(lin) + (unembedded[:, i]*unembedded[:, j]).dot(quad) + off num_samples, num_variables = unembedded.shape datatypes = [('sample', np.dtype(np.int8), (num_variables,)), ('energy', energies.dtype)] datatypes.extend((name, record[name].dtype, record[name].shape[1:]) for name in record.dtype.names if name not in {'sample', 'energy'}) data = np.rec.array(np.empty(num_samples, dtype=datatypes)) data.sample = unembedded data.energy = energies for name in record.dtype.names: if name not in {'sample', 'energy'}: data[name] = record[name][idxs] return Response(data, variables, target_response.info, target_response.vartype)
def sample(self, bqm): """Sample from a binary quadratic model. Args: bqm (:obj:`~dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. Returns: :obj:`~dimod.Response`: A `dimod` :obj:`.~dimod.Response` object. Examples: This example provides samples for a two-variable Ising model. >>> import dimod >>> sampler = dimod.ExactSolver() >>> bqm = dimod.BinaryQuadraticModel({0: 0.0, 1: 1.0}, {(0, 1): 0.5}, -0.5, dimod.SPIN) >>> response = sampler.sample(bqm) >>> response.data_vectors['energy'] array([-1., -2., 1., 0.]) """ M = bqm.binary.to_numpy_matrix() off = bqm.binary.offset if M.shape == (0, 0): return Response.empty(bqm.vartype) sample = np.zeros((len(bqm), ), dtype=bool) # now we iterate, flipping one bit at a time until we have # traversed all samples. This is a Gray code. # https://en.wikipedia.org/wiki/Gray_code def iter_samples(): sample = np.zeros((len(bqm)), dtype=bool) energy = 0.0 yield sample.copy(), energy + off for i in range(1, 1 << len(bqm)): v = _ffs(i) # flip the bit in the sample sample[v] = not sample[v] # for now just calculate the energy, but there is a more clever way by calculating # the energy delta for the single bit flip, don't have time, pull requests # appreciated! energy = sample.dot(M).dot(sample.transpose()) yield sample.copy(), float(energy) + off samples, energies = zip(*iter_samples()) response = Response.from_matrix(np.matrix(samples, dtype='int8'), {'energy': energies}, vartype=Vartype.BINARY) # make sure the response matches the given vartype, in-place. response.change_vartype(bqm.vartype, inplace=True) return response