Source code for dwave.system.composites.reversecomposite

# Copyright 2019 D-Wave Systems Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#
# =============================================================================
"""
Composites that do batch operations for reverse annealing.
"""

try:
    import collections.abc as abc
except ImportError:
    import collections as abc

import dimod
import numpy as np

__all__ = 'ReverseAdvanceComposite', 'ReverseBatchStatesComposite'


[docs]class ReverseAdvanceComposite(dimod.ComposedSampler): """ Composite that reverse anneals an initial sample through a sequence of anneal schedules. If you do not specify an initial sample, a random sample is used for the first submission. By default, each subsequent submission selects the most-found lowest-energy sample as its initial state. If you set reinitialize_state to False, which makes each submission behave like a random walk, the subsequent submission selects the last returned sample as its initial state. Args: sampler (:obj:`dimod.Sampler`): A dimod sampler. Examples: This example runs 100 reverse anneals each for three schedules on a problem constructed by setting random :math:`\pm 1` values on a clique (complete graph) of 15 nodes, minor-embedded on a D-Wave system using the :class:`.DWaveCliqueSampler` sampler. >>> import dimod >>> from dwave.system import DWaveCliqueSampler, ReverseAdvanceComposite ... >>> sampler = DWaveCliqueSampler() # doctest: +SKIP >>> sampler_reverse = ReverseAdvanceComposite(sampler) # doctest: +SKIP >>> schedule = [[[0.0, 1.0], [t, 0.5], [20, 1.0]] for t in (5, 10, 15)] ... >>> bqm = dimod.generators.ran_r(1, 15) >>> init_samples = {i: -1 for i in range(15)} >>> sampleset = sampler_reverse.sample(bqm, ... anneal_schedules=schedule, ... initial_state=init_samples, ... num_reads=100, ... reinitialize_state=True) # doctest: +SKIP """ def __init__(self, child_sampler): self._children = [child_sampler] @property def children(self): return self._children @property def parameters(self): param = self.child.parameters.copy() param['schedules'] = [] return param @property def properties(self): return {'child_properties': self.child.properties.copy()}
[docs] def sample(self, bqm, anneal_schedules=None, **parameters): """Sample the binary quadratic model using reverse annealing along a given set of anneal schedules. Args: bqm (:obj:`dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. anneal_schedules (list of lists): Anneal schedules in order of submission. Each schedule is formatted as a list of [time, s] pairs initial_state (dict, optional): the state to reverse anneal from. If not provided, it will be randomly generated **parameters: Parameters for the sampling method, specified by the child sampler. Returns: :obj:`dimod.SampleSet` that has initial_state and schedule_index fields. Examples: This example runs 100 reverse anneals each for three schedules on a problem constructed by setting random :math:`\pm 1` values on a clique (complete graph) of 15 nodes, minor-embedded on a D-Wave system using the :class:`.DWaveCliqueSampler` sampler. >>> import dimod >>> from dwave.system import DWaveCliqueSampler, ReverseAdvanceComposite ... >>> sampler = DWaveCliqueSampler() # doctest: +SKIP >>> sampler_reverse = ReverseAdvanceComposite(sampler) # doctest: +SKIP >>> schedule = [[[0.0, 1.0], [t, 0.5], [20, 1.0]] for t in (5, 10, 15)] ... >>> bqm = dimod.generators.ran_r(1, 15) >>> init_samples = {i: -1 for i in range(15)} >>> sampleset = sampler_reverse.sample(bqm, ... anneal_schedules=schedule, ... initial_state=init_samples, ... num_reads=100, ... reinitialize_state=True) # doctest: +SKIP """ child = self.child if anneal_schedules is None: return child.sample(bqm, **parameters) vartype_values = list(bqm.vartype.value) if 'initial_state' not in parameters: initial_state = dict(zip(list(bqm.variables), np.random.choice(vartype_values, len(bqm)))) else: initial_state = parameters.pop('initial_state') if not isinstance(initial_state, abc.Mapping): raise TypeError("initial state provided must be a dict, but received {}".format(initial_state)) if 'reinitialize_state' not in parameters: parameters['reinitialize_state'] = True if "answer_mode" in child.parameters: parameters['answer_mode'] = 'histogram' vectors = {} for schedule_idx, anneal_schedule in enumerate(anneal_schedules): sampleset = child.sample(bqm, anneal_schedule=anneal_schedule, initial_state=initial_state, **parameters) # update vectors initial_state, _ = dimod.as_samples(initial_state) vectors = _update_data_vector(vectors, sampleset, {'initial_state': [initial_state[0]] * len(sampleset.record.energy), 'schedule_index': [schedule_idx] * len(sampleset.record.energy)}) if schedule_idx+1 == len(anneal_schedules): # no need to create the next initial state - last iteration break # prepare the initial state for the next iteration if parameters['reinitialize_state']: # if reinitialize is on, choose the lowest energy, most probable state for next iteration ground_state_energy = sampleset.first.energy lowest_energy_samples = sampleset.record[sampleset.record.energy == ground_state_energy] lowest_energy_samples.sort(order='num_occurrences') initial_state = dict(zip(sampleset.variables, lowest_energy_samples[-1].sample)) else: # if not reinitialized, take the last state as the next initial state initial_state = dict(zip(sampleset.variables, sampleset.record.sample[-1])) samples = vectors.pop('sample') return dimod.SampleSet.from_samples((samples, bqm.variables), bqm.vartype, info={'anneal_schedules': anneal_schedules}, **vectors)
[docs]class ReverseBatchStatesComposite(dimod.ComposedSampler): """Composite that reverse anneals from multiple initial samples. Each submission is independent from one another. Args: sampler (:obj:`dimod.Sampler`): A dimod sampler. Examples: This example runs 100 reverse anneals each from two initial states on a problem constructed by setting random :math:`\pm 1` values on a clique (complete graph) of 15 nodes, minor-embedded on a D-Wave system using the :class:`.DWaveCliqueSampler` sampler. >>> import dimod >>> from dwave.system import DWaveCliqueSampler, ReverseBatchStatesComposite ... >>> sampler = DWaveCliqueSampler() # doctest: +SKIP >>> sampler_reverse = ReverseBatchStatesComposite(sampler) # doctest: +SKIP >>> schedule = [[0.0, 1.0], [10.0, 0.5], [20, 1.0]] ... >>> bqm = dimod.generators.ran_r(1, 15) >>> init_samples = [{i: -1 for i in range(15)}, {i: 1 for i in range(15)}] >>> sampleset = sampler_reverse.sample(bqm, ... anneal_schedule=schedule, ... initial_states=init_samples, ... num_reads=100, ... reinitialize_state=True) # doctest: +SKIP """ def __init__(self, child_sampler): self._children = [child_sampler] @property def children(self): return self._children @property def parameters(self): param = self.child.parameters.copy() param['initial_states'] = [] return param @property def properties(self): return {'child_properties': self.child.properties.copy()}
[docs] def sample(self, bqm, **parameters): """Sample the binary quadratic model using reverse annealing from multiple initial states. Args: bqm (:obj:`dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. **parameters: Parameters for the sampling method, specified by the child sampler. Returns: :obj:`dimod.SampleSet` that has initial_state field. Examples: This example runs 100 reverse anneals each from two initial states on a problem constructed by setting random :math:`\pm 1` values on a clique (complete graph) of 15 nodes, minor-embedded on a D-Wave system using the :class:`.DWaveCliqueSampler` sampler. >>> import dimod >>> from dwave.system import DWaveCliqueSampler, ReverseBatchStatesComposite ... >>> sampler = DWaveCliqueSampler() # doctest: +SKIP >>> sampler_reverse = ReverseBatchStatesComposite(sampler) # doctest: +SKIP >>> schedule = [[0.0, 1.0], [10.0, 0.5], [20, 1.0]] ... >>> bqm = dimod.generators.ran_r(1, 15) >>> init_samples = [{i: -1 for i in range(15)}, {i: 1 for i in range(15)}] >>> sampleset = sampler_reverse.sample(bqm, ... anneal_schedule=schedule, ... initial_states=init_samples, ... num_reads=100, ... reinitialize_state=True) # doctest: +SKIP """ child = self.child if 'initial_states' not in parameters: return child.sample(bqm, **parameters) initial_states = parameters.pop('initial_states') # there is gonna be way too much data generated - better to histogram them if possible if "answer_mode" in child.parameters: parameters['answer_mode'] = 'histogram' # prepare data fields for the new sampleset object vectors = {} for initial_state in initial_states: if not isinstance(initial_state, dict): initial_state = dict(zip(bqm.variables, initial_state)) sampleset = child.sample(bqm, initial_state=initial_state, **parameters) initial_state_, _ = dimod.as_samples(initial_state) vectors = _update_data_vector(vectors, sampleset, {'initial_state': [initial_state_[0]] * len(sampleset.record.energy)}) samples = vectors.pop('sample') return dimod.SampleSet.from_samples((samples, bqm.variables), bqm.vartype, info={}, **vectors)
def _update_data_vector(vectors, sampleset, additional_parameters=None): var_names = sampleset.record.dtype.names for name in var_names: try: vectors[name] = vectors[name] + list(sampleset.record[name]) except KeyError: vectors[name] = list(sampleset.record[name]) for key, val in additional_parameters.items(): if key not in var_names: try: vectors[key] = vectors[key] + list(val) except KeyError: vectors[key] = list(val) return vectors