Beispiel #1
0
def test_puso_quso_equal():

    random.seed(321)
    quso = {(i, j): random.random() for i in range(7) for j in range(7)}
    quso.update({(i, ): random.random() for i in range(7)})
    quso[()] = random.random()
    for sol in itertools.product((-1, 1), repeat=7):
        assert_allclose(puso_value(sol, quso), quso_value(sol, quso))
Beispiel #2
0
def test_pubo_puso_equal():

    random.seed(518)
    pubo = {(i, j, k): random.random()
            for i in range(7) for j in range(7) for k in range(7)}
    pubo.update({(i, j): random.random() for i in range(7) for j in range(7)})
    pubo.update({(i, ): random.random() for i in range(7)})
    pubo[()] = random.random()
    for sol in itertools.product((0, 1), repeat=7):
        assert_allclose(pubo_value(sol, pubo),
                        puso_value(boolean_to_spin(sol), pubo_to_puso(pubo)))
Beispiel #3
0
    def schedule_update(self, schedule, in_order=False, seed=None):
        """schedule_update.

        Update the simulation with a schedule.

        Parameters
        ----------
        schedule : iterable of tuples.
            Each element in ``schedule`` is a pair ``(T, n)`` which designates
            a temperature and a number of updates. See `Notes` below.
        in_order : bool (optional, defaults to False).
            Whether to iterate through the variables in order or randomly
            during an update step. When ``in_order`` is False, the simulation
            is more physically realistic, but when using the Simulation for
            annealing, often it is better to have ``in_order = True``.
        seed : number (optional, defaults to None).
            The number to seed ``random`` with. If ``seed is None``, then
            ``random.seed`` will not be called.

        Notes
        -----
        The following two code blocks perform exactly the same thing.

        >>> sim = PUSOSimulation(10)
        >>> for T in (3, 2):
        >>>     sim.update(T, 100)
        >>> sim.update(1, 50)

        >>> sim = PUSOSimulation(10)
        >>> schedule = (3, 100), (2, 100), (1, 50)
        >>> sim.schedule_update(schedule)

        """
        if seed is not None:
            random.seed(seed)

        for T, n in schedule:
            if n < 0:
                raise ValueError("Cannot update a negative number of times")
            for _ in range(n):
                self._add_past_state()

                vars_to_update = (self._variables if in_order else
                                  random.choices(self._variables,
                                                 k=len(self._variables)))

                for i in vars_to_update:
                    # the change in energy from flipping variable i is equal
                    # to -2 * (the energy of the subgraph depending on i)
                    dE = -2 * puso_value(self._state, self._subgraphs[i])
                    if dE <= 0 or (T and random.random() < exp(-dE / T)):
                        self._state[i] *= -1
Beispiel #4
0
    def runtests(self):

        assert self.problem.solve_bruteforce() == self.solution

        e, sol = solve_qubo_bruteforce(self.problem.to_qubo())
        assert self.is_valid(e, sol, False)

        e, sol = solve_quso_bruteforce(self.problem.to_quso())
        assert self.is_valid(e, sol, True)

        for deg in (None, ) + tuple(range(2, self.problem.degree + 1)):

            e, sol = solve_puso_bruteforce(self.problem.to_puso(deg))
            assert self.is_valid(e, sol, True)

            e, sol = solve_pubo_bruteforce(self.problem.to_pubo(deg))
            assert self.is_valid(e, sol, False)

        assert (self.problem.value(self.solution) == puso_value(
            self.solution, self.problem) == e)
Beispiel #5
0
def _anneal_spin(model, spin_simulation, num_anneals=1,
                 anneal_duration=1000, initial_state=None,
                 temperature_range=None, schedule='geometric',
                 in_order=True, seed=None):
    """_anneal_spin.

    Run a simulated annealing algorithm to try to find the minimum of the spin
    model given by ``model``. ``_anneal_spin`` uses a cooling schedule with the
    ``spin_simulation`` object. Please see all of the parameters for details.

    Both ``qv.sim.anneal_puso`` and ``qv.sim.anneal_quso`` run through this
    function. Since ``qv.sim.QUSOSimulation`` is faster than
    ``qv.sim.PUSOSimulation``, we send in different simulation objects for
    ``anneal_quso`` and ``anneal_puso``.

    Parameters
    ----------
    model : dict, or any type in ``qubovert.SPIN_MODELS``.
        Maps spin labels to their values in the Hamiltonian.
        Please see the docstrings of any of the objects in
        ``qubovert.SPIN_MODELS`` to see how ``H`` should be formatted.
    spin_simulation : qv.sim.PUSOSimulation or qv.sim.QUSOSimulation object.
        Should be a ``qv.sim.QUSOSimulation`` object if this function is called
        from ``qv.sim.anneal_quso``, or a ``qv.sim.PUSOSimulation`` object if
        this function is called from ``qv.sim.anneal_puso``.
    num_anneals : int >= 1 (optional, defaults to 1).
        The number of times to run the simulated annealing algorithm.
    anneal_duration : int >= 1 (optional, defaults to 1000).
        The total number of updates to the simulation during the anneal.
        This is related to the amount of time we spend in the cooling schedule.
        If an explicit schedule is provided, then ``anneal_duration`` will be
        ignored.
    initial_state : dict (optional, defaults to None).
        The initial state to start the anneal in. ``initial_state`` must map
        the spin label names to their values in {1, -1}. If ``initial_state``
        is None, then a random state will be chosen to start each anneal.
        Otherwise, ``initial_state`` will be the starting state for all of the
        anneals.
    temperature_range : tuple (optional, defaults to None).
        The temperature to start and end the anneal at.
        ``temperature = (T0, Tf)``. ``T0`` must be >= ``Tf``. To see more
        details on picking a temperature range, please see the function
        ``qubovert.sim.anneal_temperature_range``. If ``temperature_range`` is
        None, then it will by default be set to
        ``T0, Tf = qubovert.sim.anneal_temperature_range(H, spin=True)``.
        Note that a temperature can only be zero if ``schedule`` is explicitly
        given or if ``schedule`` is linear.
    schedule : str or iterable of tuple (optional, defaults to ``'geometric'``)
        What type of cooling schedule to use. If ``schedule == 'linear'``, then
        the cooling schedule will be a linear interpolation between the values
        in ``temperature_range``. If ``schedule == 'geometric'``, then the
        cooling schedule will be a geometric interpolation between the values
        in ``temperature_range``. Otherwise, you can supply an explicit
        schedule. In this case, ``schedule`` should be an iterable of tuples,
        where each tuple is a ``(T, n)`` pair, where ``T`` denotes the
        temperature to update the simulation, and ``n`` denote the number of
        times to update the simulation at that temperature. This schedule
        will be sent directly into the
        ``qubovert.sim.PUSOSimulation.schedule_update`` method.
    in_order : bool (optional, defaults to True).
        Whether to iterate through the variables in order or randomly
        during an update step. When ``in_order`` is False, the simulation
        is more physically realistic, but when using the simulation for
        annealing, often it is better to have ``in_order = True``.
    seed : number (optional, defaults to None).
        The number to seed Python's builtin ``random`` module with. If
        ``seed is None``, then ``random.seed`` will not be called.

    Returns
    -------
    res : qubovert.sim.AnnealResults object.
        ``res`` contains information on the final states of the simulations.
        See Examples below for an example of how to read from ``res``.
        See ``help(qubovert.sim.AnnealResults)`` for more info.

    Raises
    ------
    ValueError
        If the ``schedule`` argument provided is formatted incorrectly. See the
        Parameters section.
    ValueError
        If the initial temperature is less than the final temperature.

    Warns
    -----
    qubovert.utils.QUBOVertWarning
        If both the ``temperature_range`` and explicit ``schedule`` arguments
        are provided.

    """
    if seed is not None:
        random.seed(seed)

    if schedule in ('linear', 'geometric'):
        T0, Tf = temperature_range or anneal_temperature_range(model,
                                                               spin=True)
        if T0 < Tf:
            raise ValueError("The final temperature must be less than the "
                             "initial temperature")

        # in the case that H is empty or just an offset and the user didn't
        # supply a temperature range, then T0 and Tf will be 0.
        if temperature_range is None and T0 == Tf == 0:
            T0 = Tf = 1
        Ts = (
            np.linspace(T0, Tf, anneal_duration) if schedule == 'linear' else
            np.geomspace(T0, Tf, anneal_duration)
        )
        schedule = tuple((T, 1) for T in Ts)
    elif isinstance(schedule, str):
        raise ValueError(
            "Invalid schedule. Must be either 'linear', 'geometric', or an "
            "explicit temperature schedule. See the docstring for more info."
        )
    elif temperature_range:
        QUBOVertWarning.warn(
            "Both a temperature range and an explicit schedule was provided. "
            "The temperature range will be ignored and the schedule used "
            "instead."
        )

    sim = spin_simulation(model, initial_state)

    result = AnnealResults(True)
    for _ in range(num_anneals):
        if initial_state is None:
            sim.set_state({v: random.choice((-1, 1)) for v in sim._variables})
        sim.schedule_update(
            schedule, in_order=in_order,
            seed=random.randint(0, 1 << 16) if seed is not None else None
        )
        state = sim.state
        result.add_state(state, puso_value(state, model))
        sim.reset()

    return result