def test_puso_to_pubo_to_puso(): puso = { (0, 1): -4, (0, 2): 3, (): -2, (0, ): 1, (2, ): -2, (0, 1, 2): 3, (0, 2, 3): -1 } assert puso == pubo_to_puso(puso_to_pubo(puso)) # type asserting assert type(puso_to_pubo(puso)) == PUBO assert type(puso_to_pubo(PUSOMatrix(puso))) == PUBOMatrix assert type(puso_to_pubo(PUSO(puso))) == PUBO puso = { ('0', 1): -4, ('0', '2'): 3, (): -2, ('0', ): 1, ('2', ): -2, ('0', 1, '2'): 3, ('0', '2', 3): -1, ('2', 0, 0, '1', 0): -2, (0, 1, 1, 0, 3, 0, 1, 1, 3, 2, 3): -8 } # need to reformat qubo so it is sorted with the same hash assert PUSO(puso) == pubo_to_puso(puso_to_pubo(puso)) # type asserting assert type(puso_to_pubo(puso)) == PUBO assert type(puso_to_pubo(PUSO(puso))) == PUBO
def test_pubo_to_puso_to_pubo(): pubo = { (0, ): 1, (0, 1): 1, (1, ): -1, (1, 2): .5, (): -2, (2, ): 1, (0, 2, 3): -3, (0, 1, 2): -2 } assert pubo == puso_to_pubo(pubo_to_puso(pubo)) pubo = { ('0', ): 1, ('0', 1): 1, (1, ): -1, (1, '2'): .5, (): -2, ('2', ): 1, ('0', '2', 3): -3, ('0', 1, '2'): -2, ('0', '0', 1, '0', '2', '2'): -9, (4, 2, 4, 0, 2, 0, 0): 3 } # need to reformat pubo so it is sorted with the same hash and squashed key assert PUBO(pubo) == puso_to_pubo(pubo_to_puso(pubo))
def __init__(self, P, initial_state=None, memory=0): """__init__. Parameters ---------- P : dict or type in ``qubovert.BOOLEAN_MODELS``. The PUBO to simulate. This should map tuples of boolean variable labels to their respective coefficient in the objective function. For more information, see the docstrings for any of the models in ``qubovert.BOOLEAN_MODELS``. initial_state : dict (optional, defaults to None). The initial state to start the simulation in. ``initial_state`` should map boolean label names to their initial values, where each value is either 0 or 1. If ``initial_state`` is None, then it will be initialized to all 0s. memory : int >= 0 (optional, defaults to 0). During the simulation, we keep a list of the most recent ``memory`` states that the simulation was in. These can be accessed with ``self.get_past_states(number_of_states)``. """ model = pubo_to_puso(P) if initial_state is None: initial_state = {v: 0 for v in model._variables} super().__init__(model, initial_state, memory)
def test_pcso_ne_constraint(): for i in range(1 << 3): P = pubo_to_puso(integer_var('a', 3)) - i H = PCSO().add_constraint_ne_zero(P, log_trick=False) for sol in H.solve_bruteforce(True): assert P.value(sol)
def test_puso_degree_reduction_pairs(): puso = pubo_to_puso({ ('x0', 'x1'): -1, ('x1', ): 1, ('x1', 'x2'): -1, ('x2', ): 1, ('x3', 'x2'): -1, ('x3', ): 1, ('x4', 'x3'): -1, ('x4', ): 1, ('x4', 'x5'): -1, ('x5', ): 1, ('x5', 'x6'): -1, ('x6', ): 1, ('x7', 'x6'): -1, ('x7', ): 1, ('x8', 'x7'): -1, ('x8', ): 1, ('x9', 'x8'): -1, ('x9', ): 1 })**2 pairs = {('x0', 'x1'), ('x1', 'x2'), ('x2', 'x3'), ('x3', 'x4'), ('x4', 'x5'), ('x5', 'x6'), ('x6', 'x7'), ('x7', 'x8'), ('x8', 'x9')} quso1 = puso.to_quso() quso2 = puso.to_quso(pairs=pairs) assert quso1.num_binary_variables - puso.num_binary_variables > 9 assert quso2.num_binary_variables - puso.num_binary_variables == 9 qubo1 = puso.to_qubo() qubo2 = puso.to_qubo(pairs=pairs) assert qubo1.num_binary_variables - puso.num_binary_variables > 9 assert qubo2.num_binary_variables - puso.num_binary_variables == 9
def test_pcso_le_constraint(): lam = Symbol("lam") H = PCSO( pubo_to_puso({ ('a', ): -1, ('b', ): 2, ('a', 'b'): -3, ('b', 'c'): -4, (): -2, ('d', ): -1 })) H.add_constraint_le_zero(pubo_to_puso({ ('a', ): 1, ('b', ): 1, ('b', 'c'): 1, ('d', ): 1, (): -3 }), lam=lam, log_trick=False) solution = boolean_to_spin({'c': 1, 'b': 1, 'a': 1, 'd': 0}) obj = -8 problem = H.subs(lam, .5) sol = problem.remove_ancilla_from_solution(problem.solve_bruteforce()) assert all((problem.is_solution_valid(sol), sol == solution)) e, sol = solve_pubo_bruteforce(problem.to_pubo()) sol = problem.convert_solution(sol, spin=False) sol = problem.remove_ancilla_from_solution(sol) assert all((not problem.is_solution_valid(sol), sol != solution, not allclose(e, obj))) problem = H.subs(lam, 10) sol = problem.solve_bruteforce() sol = problem.remove_ancilla_from_solution(sol) assert all((problem.is_solution_valid(sol), sol == solution)) e, sol = solve_pubo_bruteforce(problem.to_pubo()) sol = problem.convert_solution(sol) sol = problem.remove_ancilla_from_solution(sol) assert all( (problem.is_solution_valid(sol), sol == solution, allclose(e, obj)))
def test_pcso_ne_constraint_logtrick(): for i in range(1 << 4): P = pubo_to_puso(integer_var('a', 4)) - i H = PCSO().add_constraint_ne_zero(P) for sol in H.solve_bruteforce(True): assert P.value(sol) for i in range(1 << 4): P = pubo_to_puso(integer_var('a', 4)) - i H = PCSO(P).add_constraint_ne_zero(P, lam=0) for sol in H.solve_bruteforce(True): assert P.value(sol) for i in range(1 << 2): P = pubo_to_puso(integer_var('a', 2)) - i H = PCSO().add_constraint_ne_zero(P) for sol in solve_puso_bruteforce(H, True)[1]: assert P.value(sol)
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)))
def test_pcso_eq_constraint(): lam = Symbol('lam') H = PCSO( pubo_to_puso({ ('a', ): -1, ('b', ): 2, ('a', 'b'): -3, ('b', 'c'): -4, (): -2 })) H.add_constraint_eq_zero(pubo_to_puso({ ('a', ): 1, ('b', ): 1, ('b', 'c'): -1 }), lam=lam) solution = boolean_to_spin({'c': 1, 'b': 1, 'a': 0}) obj = -4 problem = H.subs(lam, 1) sol = problem.solve_bruteforce() assert all((problem.is_solution_valid(sol), sol == solution)) e, sol = solve_pubo_bruteforce(problem.to_pubo()) sol = problem.convert_solution(sol, False) assert all((not problem.is_solution_valid(sol), sol != solution, not allclose(e, obj))) problem = H.subs(lam, 10) sol = problem.solve_bruteforce() assert all((problem.is_solution_valid(sol), sol == solution)) e, sol = solve_pubo_bruteforce(problem.to_pubo()) sol = problem.convert_solution(sol) assert all( (problem.is_solution_valid(sol), sol == solution, allclose(e, obj)))
def anneal_pubo(P, num_anneals=1, anneal_duration=1000, initial_state=None, temperature_range=None, schedule='geometric', in_order=True, seed=None): """anneal_pubo. Run a simulated annealing algorithm to try to find the minimum of the PUBO given by ``P``. ``anneal_pubo`` uses a cooling schedule with the ``qubovert.sim.PUBOSimulation`` object. Please see all of the parameters for details. **Please note** that the ``qv.sim.anneal_qubo`` function performs much faster than the ``qv.sim.anneal_pubo`` function since the former is written in C and wrapped in Python. If your system has degree 2 or less, then you should use the ``qv.sim.anneal_qubo`` function! Parameters ---------- P : dict, or any type in ``qubovert.BOOLEAN_MODELS``. Maps boolean labels to their values in the objective function. Please see the docstrings of any of the objects in ``qubovert.BOOLEAN_MODELS`` to see how ``P`` should be formatted. 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 boolean label names to their values in {0, 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(P, spin=False)``. 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.PUBOSimulation.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. Example ------- Consider the example of finding the ground state of the 1D antiferromagnetic Ising chain of length 5 in boolean form. >>> import qubovert as qv >>> >>> H = sum(qv.spin_var(i) * qv.spin_var(i+1) for i in range(4)) >>> P = H.to_pubo() >>> anneal_res = qv.sim.anneal_pubo(P, num_anneals=3) >>> >>> print(anneal_res.best.value) -4 >>> print(anneal_res.best.state) {0: 0, 1: 1, 2: 0, 3: 1, 4: 0} >>> # now sort the results >>> anneal_res.sort_by_value() >>> >>> # now iterate through all of the results in the sorted order >>> for res in anneal_res: >>> print(res.value, res.state) -4, {0: 0, 1: 1, 2: 0, 3: 1, 4: 0} -4, {0: 1, 1: 0, 2: 1, 3: 0, 4: 1} -4, {0: 0, 1: 1, 2: 0, 3: 1, 4: 0} """ return anneal_puso( pubo_to_puso(P), num_anneals, anneal_duration, None if initial_state is None else boolean_to_spin(initial_state), temperature_range, schedule, in_order, seed ).to_boolean()
def anneal_temperature_range(model, start_flip_prob=0.5, end_flip_prob=0.01, spin=False): """anneal_temperature_range. Calculate the temperature to start and end an anneal of ``model``, such that at the start of the anneal there is a ``start_flip_prob`` probability that a bit is flipped despite it being energetically unfavorable, and at the end of the anneal there is a ``end_flip_prob`` probability that a bit is flipped despite it being energetically unfavorable. Parameters ---------- model : dict, or any type in ``qubovert.SPIN_MODELS`` or ``BOOLEAN_MODELS`` Dictionary mapping tuples of binary labels to their values. See any of the docstrings of a type in ``qubovert.SPIN_MODELS`` or ``BOOLEAN_MODELS`` for more info. start_flip_prob : float in [0, 1) (optional, defaults to 0.5). The desired probability that a bit flips despite it being energetically unfavorable at the start of the anneal. ``start_flip_prob`` must be greater than ``end_flip_prob``. end_flip_prob : float in [0, 1) (optional, defaults to 0.01). The desired probability that a bit flips despite it being energetically unfavorable at the end of the anneal. ``end_flip_prob`` must be less than ``start_flip_prob``. spin : bool (optional, default to False). ``spin`` should be True if ``model`` is a spin model (ie ``isinstance(model, qubovert.SPIN_MODELS)``) and should be False if ``model`` is a boolean model (ie ``isinstance(model, qubovert.BOOLEAN_MODELS)``). Returns ------- temp_range : tuple (hot, cold). The ``hot`` temperature is the temperature to start the anneal at, and the ``cold`` temperature is the temperature to end the anneal at. Note that ``hot >= cold``. """ # slight modification of _default_ising_beta_range in # https://github.com/dwavesystems/dwave-neal/blob/master/neal/sampler.py # raise exception if invalid probabilities if any(( start_flip_prob < 0, start_flip_prob >= 1, end_flip_prob < 0, end_flip_prob >= 1, )): raise ValueError("Flip probabilities must be in [0, 1)") elif end_flip_prob > start_flip_prob: raise ValueError("The starting flip probability must be greater than " "the ending flip probability.") if not spin: model = pubo_to_puso(model) # if D is a Matrix object or QUBO, PUBO, etc, then variables are defined try: # don't waste time copying (model.variables), since we never mutate it. variables = model._variables except AttributeError: variables = set(v for k in model for v in k) # if the model is empty or just an offset if not variables: return 0, 0 factor = 2 # should be this # factor = 1 # D-Wave neal does this. # calculate the approximate minimum possible change in energy by flipping # a single bit. min_del_energy = factor * min(abs(c) for k, c in model.items() if k) # calculate the approximate maximum possible change in energy by flipping # a single bit. max_del_energy = factor * max( sum(abs(c) for k, c in model.items() if v in k) for v in variables ) # now ensure that the bolzmann weight satisfy the desired probabilities. # ie exp(-del_energy / T) = prob T0 = -max_del_energy / np.log(start_flip_prob) if start_flip_prob else 0 Tf = -min_del_energy / np.log(end_flip_prob) if end_flip_prob else 0 return float(T0), float(Tf)
def test_qubo_to_quso_eq_pubo_to_puso(): qubo = {(0, ): 1, (0, 1): 1, (1, ): -1, (1, 2): .2, (): -2, (2, ): 1} assert qubo_to_quso(qubo) == pubo_to_puso(qubo)