def test_composition(self): class A(Runnable): def next(self, state): return state.updated(x=state.x + 1) class B(Runnable): def next(self, state): return state.updated(x=state.x * 7) a, b = A(), B() s = State(x=1) b1 = Branch(components=(a, b)) self.assertEqual(b1.components, (a, b)) self.assertEqual(b1.run(s).result().x, (s.x + 1) * 7) b2 = b1 | b | a self.assertEqual(b2.components, (a, b, b, a)) self.assertEqual(b2.run(s).result().x, (s.x + 1) * 7 * 7 + 1) with self.assertRaises(TypeError): a | 1 with self.assertRaises(TypeError): b1 | 1
def test_infinite_loop_stops(self): """An infinite loop can be stopped after 10 iterations.""" class Countdown(Runnable): """Countdown runnable that sets a semaphore on reaching zero.""" def __init__(self): super(Countdown, self).__init__() self.ring = threading.Event() def next(self, state): output = state.updated(cnt=state.cnt - 1) if output.cnt <= 0: self.ring.set() return output countdown = Countdown() loop = LoopWhileNoImprovement(countdown) state = loop.run(State(cnt=10)) # stop only AFTER countdown reaches zero (10 iterations) # timeout in case Countdown failed before setting the flag countdown.ring.wait(timeout=1) loop.stop() self.assertTrue(state.result().cnt <= 0)
def test_custom_qpu_params(self): bqm = dimod.BinaryQuadraticModel.from_ising({'a': 1}, {}) init = State.from_subproblem(bqm) # define a mock sampler that exposes some parameters of interest mock_sampler = mock.MagicMock() mock_sampler.parameters = { 'num_reads': [], 'chain_strength': [], 'future_proof': [] } qpu_params = dict(chain_strength=2, future_proof=True) workflow = QPUSubproblemAutoEmbeddingSampler( num_reads=10, qpu_sampler=mock_sampler, sampling_params=qpu_params) # run mock sampling workflow.run(init).result() # verify mock sampler received custom kwargs mock_sampler.sample.assert_called_once_with(bqm, num_reads=10, **qpu_params)
def test_initial_state(self): initial = State(val=10) states = States(State(val=1), State(val=2)) result = Reduce(self.Sum(), initial_state=initial).run(states).result() self.assertEqual(result.val, 10 + 1 + 2)
def test_map_lambda(self): states = States(State(cnt=1), State(cnt=2)) result = Map(Lambda(lambda _, s: s.updated(cnt=s.cnt + 1))).run(states).result() self.assertEqual(result[0].cnt, states[0].cnt + 1) self.assertEqual(result[1].cnt, states[1].cnt + 1)
def test_valid_output(self): class Component(traits.SubproblemProducing, Runnable): def next(self, state): return state.updated(subproblem=True) self.assertTrue(Component().run(State()).result().subproblem)
def next(self, states): return States(State(subsamples=1), State(subsamples=2))
def next(self, state): return States(State(embedding=1), State(embedding=2))
def next(self, states): # should return States() return State()
def error(self, exc): return State(error=True)
def test_construction(self): self.assertDictEqual(State(), dict(samples=None, problem=None)) self.assertEqual(State(samples=[1]).samples, [1]) self.assertEqual(State(problem={'a': 1}).problem, {'a': 1}) self.assertEqual(State(debug={'a': 1}).debug, {'a': 1})
def test_res(self): for val in 1, 'x', True, False, State(problem=1), lambda: None: f = Present(result=val) self.assertIsInstance(f, Future) self.assertTrue(f.done()) self.assertEqual(f.result(), val)
def test_state_api(self): states = States(State(x=1), State(y=1)) self.assertEqual(states.result(), states) self.assertEqual(states.updated(x=2), States(State(x=2), State(x=2, y=1)))
def sample(self, bqm, init_sample=None, max_iter=100, convergence=10, num_reads=1, sa_reads=1, sa_sweeps=1000, qpu_reads=100, qpu_sampler=None, max_subproblem_size=50): """Run Tabu search, Simulated annealing and QPU subproblem sampling (for high energy impact problem variables) in parallel and return the best samples. Args: bqm (:obj:`~dimod.BinaryQuadraticModel`): Binary quadratic model to be sampled from. init_sample (:class:`~dimod.SampleSet`, callable, ``None``): Initial sample set (or sample generator) used for each "read". Use a random sample for each read by default. max_iter (int): Number of iterations in the hybrid algorithm. convergence (int): Number of iterations with no improvement that terminates sampling. num_reads (int): Number of reads. Each sample is the result of a single run of the hybrid algorithm. sa_reads (int): Number of reads in the simulated annealing branch. sa_sweeps (int): Number of sweeps in the simulated annealing branch. qpu_reads (int): Number of reads in the QPU branch. qpu_sampler (:class:`dimod.Sampler`, optional, default=DWaveSampler()): Quantum sampler such as a D-Wave system. max_subproblem_size (int): Maximum size of the subproblem selected in the QPU branch. Returns: :obj:`~dimod.SampleSet`: A `dimod` :obj:`.~dimod.SampleSet` object. """ if callable(init_sample): init_state_gen = lambda: State.from_sample(init_sample(), bqm) elif init_sample is None: init_state_gen = lambda: State.from_sample(random_sample(bqm), bqm) elif isinstance(init_sample, dimod.SampleSet): init_state_gen = lambda: State.from_sample(init_sample, bqm) else: raise TypeError( "'init_sample' should be a SampleSet or a SampleSet generator") subproblem_size = min(len(bqm), max_subproblem_size) iteration = RacingBranches( InterruptableTabuSampler(), InterruptableSimulatedAnnealingProblemSampler(num_reads=sa_reads, sweeps=sa_sweeps), EnergyImpactDecomposer(size=subproblem_size, rolling=True, rolling_history=0.3, traversal='bfs') | QPUSubproblemAutoEmbeddingSampler(num_reads=qpu_reads, qpu_sampler=qpu_sampler) | SplatComposer(), ) | ArgMin() self.runnable = Loop(iteration, max_iter=max_iter, convergence=convergence) samples = [] energies = [] for _ in range(num_reads): init_state = init_state_gen() final_state = self.runnable.run(init_state) # the best sample from each run is one "read" ss = final_state.result().samples ss.change_vartype(bqm.vartype, inplace=True) samples.append(ss.first.sample) energies.append(ss.first.energy) return dimod.SampleSet.from_samples(samples, vartype=bqm.vartype, energy=energies)
class TestHybridRunnable(unittest.TestCase): bqm = dimod.BinaryQuadraticModel({}, { 'ab': 1, 'bc': 1, 'ca': -1 }, 0, dimod.SPIN) init_state = State.from_sample(min_sample(bqm), bqm) def test_generic(self): runnable = HybridRunnable(TabuSampler(), fields=('problem', 'samples')) response = runnable.run(self.init_state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().samples.record[0].energy, -3.0) def test_validation(self): with self.assertRaises(TypeError): HybridRunnable(1, 'ab') with self.assertRaises(ValueError): HybridRunnable(TabuSampler(), None) with self.assertRaises(ValueError): HybridRunnable(TabuSampler(), ('a')) self.assertIsInstance(HybridRunnable(TabuSampler(), 'ab'), HybridRunnable) self.assertIsInstance(HybridRunnable(TabuSampler(), ('a', 'b')), HybridRunnable) self.assertIsInstance(HybridRunnable(TabuSampler(), ['a', 'b']), HybridRunnable) def test_problem_sampler_runnable(self): runnable = HybridProblemRunnable(TabuSampler()) response = runnable.run(self.init_state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().samples.record[0].energy, -3.0) def test_subproblem_sampler_runnable(self): runnable = HybridSubproblemRunnable(TabuSampler()) state = self.init_state.updated(subproblem=self.bqm) response = runnable.run(state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().subsamples.record[0].energy, -3.0) def test_runnable_composition(self): runnable = IdentityDecomposer() | HybridSubproblemRunnable( TabuSampler()) | IdentityComposer() response = runnable.run(self.init_state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().samples.record[0].energy, -3.0) def test_racing_workflow_with_oracle_subsolver(self): workflow = hybrid.LoopUntilNoImprovement(hybrid.RacingBranches( hybrid.InterruptableTabuSampler(), hybrid.EnergyImpactDecomposer(size=1) | HybridSubproblemRunnable(dimod.ExactSolver()) | hybrid.SplatComposer()) | hybrid.ArgMin(), convergence=3) state = State.from_sample(min_sample(self.bqm), self.bqm) response = workflow.run(state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().samples.record[0].energy, -3.0) def test_sampling_parameters_filtering(self): class Sampler(dimod.ExactSolver): """Exact solver that fails if a sampling parameter is provided.""" parameters = {} def sample(self, bqm): return super().sample(bqm) workflow = hybrid.LoopUntilNoImprovement(hybrid.RacingBranches( hybrid.InterruptableTabuSampler(), hybrid.EnergyImpactDecomposer(size=1) | HybridSubproblemRunnable(Sampler()) | hybrid.SplatComposer()) | hybrid.ArgMin(), convergence=3) state = State.from_sample(min_sample(self.bqm), self.bqm) response = workflow.run(state) self.assertIsInstance(response, concurrent.futures.Future) self.assertEqual(response.result().samples.record[0].energy, -3.0)
def test_basic_runnable(self): runnable = Lambda(lambda _, s: s.updated(c=s.a * s.b)) state = State(a=2, b=3) result = runnable.run(state).result() self.assertEqual(result.c, state.a * state.b)
def test_input_type_invariant(self): inp1 = State(x=1) self.assertEqual(Identity().run(inp1).result(), inp1) inp2 = States(State(x=1), State(x=2)) self.assertEqual(Identity().run(inp2).result(), inp2)
def test_updated(self): a = SampleSet.from_samples([1, 0, 1], 'SPIN', 0) b = SampleSet.from_samples([0, 1, 0], 'SPIN', 0) s1 = State(samples=a) s2 = State(samples=b, emb={'a': {'b': 1}}, debug={'x': 1}) s3 = State(debug={'x': {'y': {'z': [1]}}}) # test simple replace self.assertDictEqual(s1.updated(), s1) self.assertDictEqual(s1.updated(samples=b), State(samples=b)) self.assertDictEqual(s2.updated(emb={'b': 1}).emb, {'b': 1}) self.assertDictEqual( s1.updated(samples=b, debug=dict(x=1), emb={'a': { 'b': 1 }}), s2) # test recursive merge of `debug` self.assertDictEqual(s1.updated(debug=dict(x=1)).debug, {'x': 1}) self.assertDictEqual(s2.updated(debug=dict(x=2)).debug, {'x': 2}) self.assertDictEqual( s2.updated(debug=dict(y=2)).debug, { 'x': 1, 'y': 2 }) self.assertDictEqual( s2.updated(debug=dict(y=2)).debug, { 'x': 1, 'y': 2 }) self.assertDictEqual( s3.updated(debug={ 'x': { 'y': { 'z': [2] } } }).debug, {'x': { 'y': { 'z': [2] } }}) self.assertDictEqual( s3.updated(debug={ 'x': { 'y': { 'w': 2 } } }).debug, {'x': { 'y': { 'z': [1], 'w': 2 } }}) # test clear self.assertEqual(s2.updated(emb=None).emb, None) self.assertEqual(s2.updated(debug=None).debug, None)
class TestComponentDecomposer(unittest.TestCase): bqm = dimod.BinaryQuadraticModel({ 'a': 2, 'b': -1, 'd': 1 }, { 'bc': 1, 'cd': 1 }, 0, dimod.SPIN) state = State.from_sample(random_sample(bqm), bqm) def test_default(self): decomposer = ComponentDecomposer() state1 = decomposer.next(self.state) self.assertIn(dict(state1.subproblem.linear), ({ 'a': 2 }, { 'b': -1, 'c': 0, 'd': 1 })) state2 = decomposer.next(state1) self.assertIn(dict(state2.subproblem.linear), ({ 'a': 2 }, { 'b': -1, 'c': 0, 'd': 1 })) self.assertNotEqual(dict(state1.subproblem.linear), dict(state2.subproblem.linear)) state3 = decomposer.next( state2) # silent_rewind=True, so rewind w/o raising an assertion self.assertIn(dict(state3.subproblem.linear), ({ 'a': 2 }, { 'b': -1, 'c': 0, 'd': 1 })) def test_sort(self): decomposer = ComponentDecomposer(key=len) state1 = decomposer.next(self.state) self.assertDictEqual(dict(state1.subproblem.linear), { 'b': -1, 'c': 0, 'd': 1 }) def test_sort_reverse(self): decomposer = ComponentDecomposer(key=len, reverse=False) state1 = decomposer.next(self.state) self.assertDictEqual(dict(state1.subproblem.linear), {'a': 2}) def test_no_silent_rewind(self): decomposer = ComponentDecomposer(silent_rewind=False) state1 = decomposer.next(self.state) state2 = decomposer.next(state1) with self.assertRaises(EndOfStream): state3 = decomposer.next(state2) def test_key_func(self): def sum_linear_biases(component): total = 0 for v in component: total += self.bqm.get_linear(v) return total decomposer = ComponentDecomposer(key=sum_linear_biases) state1 = decomposer.next(self.state) self.assertDictEqual(dict(state1.subproblem.linear), {'a': 2}) def test_no_rolling(self): decomposer = ComponentDecomposer(rolling=False, key=len) state1 = decomposer.next(self.state) self.assertDictEqual(dict(state1.subproblem.linear), { 'b': -1, 'c': 0, 'd': 1 }) state2 = decomposer.next(state1) self.assertDictEqual(dict(state2.subproblem.linear), { 'b': -1, 'c': 0, 'd': 1 }) def test_one_component(self): bqm = dimod.BinaryQuadraticModel({ 'a': 1, 'b': -1 }, {'ab': 1}, 0, dimod.SPIN) state = State.from_sample(random_sample(bqm), bqm) decomposer = ComponentDecomposer() state1 = decomposer.next(state) self.assertEqual(bqm, state1.subproblem) def test_empty(self): bqm = dimod.BinaryQuadraticModel(dimod.SPIN) state = State.from_sample(random_sample(bqm), bqm) decomposer = ComponentDecomposer() state1 = decomposer.next(state) self.assertEqual(bqm, state1.subproblem)
def test_set_state_var(self): """State variable is properly set/reset/updated.""" self.assertEqual(Const(x=1).run(State()).result().x, 1) self.assertEqual(Const(x=1).run(State(x=0)).result().x, 1) self.assertEqual(Const(x=None).run(State(x=0)).result().x, None)
def next(self, state): # FAIL: embedding is missing in second state return States(State(embedding=1), State())
def test_composition(self): class A(Runnable): def next(self, state): return state.updated(x=state.x + 1) class B(Runnable): def next(self, state): return state.updated(x=state.x * 7) a, b = A(), B() # single branch b1 = Branches(a) ss = States(State(x=1)) res = b1.run(ss).result() self.assertEqual(b1.branches, (a, )) self.assertEqual(len(res), 1) self.assertEqual(res[0].x, ss[0].x + 1) # two branches, explicit and implicit construction for b2 in [Branches(a, b), a & b]: ss = States(State(x=1), State(x=1)) res = b2.run(ss).result() self.assertEqual(b2.branches, (a, b)) self.assertEqual(len(res), 2) self.assertEqual(res[0].x, ss[0].x + 1) self.assertEqual(res[1].x, ss[1].x * 7) # appending a branch to branches b3 = b2 & a ss = States(*[State(x=1) for _ in range(3)]) res = b3.run(ss).result() self.assertEqual(b3.branches, (a, b, a)) self.assertEqual(len(res), 3) self.assertEqual(res[0].x, ss[0].x + 1) self.assertEqual(res[1].x, ss[1].x * 7) self.assertEqual(res[2].x, ss[2].x + 1) # prepending a branch to branches b4 = b & b2 ss = States(*[State(x=1) for _ in range(3)]) res = b4.run(ss).result() self.assertEqual(b4.branches, (b, a, b)) self.assertEqual(len(res), 3) self.assertEqual(res[0].x, ss[0].x * 7) self.assertEqual(res[1].x, ss[1].x + 1) self.assertEqual(res[2].x, ss[2].x * 7) # invalid type with self.assertRaises(TypeError): b & 1 with self.assertRaises(TypeError): b1 & 1
def test_valid_input(self): class Component(traits.SubproblemIntaking, Runnable): def next(self, state): return State() self.assertTrue(Component().run(State(subproblem=None)).result())
def test_basic(self): s = State(x=1) self.assertEqual(Dup(0).run(s).result(), States()) self.assertEqual(Dup(1).run(s).result(), States(s)) self.assertEqual(Dup(2).run(s).result(), States(s, s))
def next(self, state): return State()
def test_immutability(self): s = State(x=1) ss = Dup(1).run(s).result() s.x = 2 self.assertEqual(ss[0].x, 1)
import dimod from hybrid.samplers import (QPUSubproblemAutoEmbeddingSampler, InterruptableTabuSampler) from hybrid.decomposers import EnergyImpactDecomposer from hybrid.composers import SplatComposer from hybrid.core import State from hybrid.flow import RacingBranches, ArgMin, Loop from hybrid.utils import min_sample # load a problem problem = sys.argv[1] with open(problem) as fp: bqm = dimod.BinaryQuadraticModel.from_coo(fp) # define the solver iteration = RacingBranches( InterruptableTabuSampler(), EnergyImpactDecomposer(max_size=50, rolling=True, rolling_history=0.15) | QPUSubproblemAutoEmbeddingSampler() | SplatComposer()) | ArgMin() main = Loop(iteration, max_iter=10, convergence=3) # run solver init_state = State.from_sample(min_sample(bqm), bqm) solution = main.run(init_state).result() # show results print("Solution: sample={s.samples.first}".format(s=solution))
def test_basic(self): states = States(State(val=1), State(val=2), State(val=3)) result = Reduce(self.Sum()).run(states).result() self.assertIsInstance(result, State) self.assertEqual(result.val, 1 + 2 + 3)