Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    def sample(self, bqm):
        M = to_numpy_matrix(bqm.binary)

        sample = np.zeros((len(bqm), ), dtype=bool)

        response = Response(Vartype.BINARY)

        # 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.add_samples_from(np.asarray(samples), energies)

        # finally make sure the response matches the given vartype, in-place.
        response.change_vartype(bqm.vartype)

        return response
    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)
Beispiel #6
0
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)