def test_default_fold(self): bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) states = States( State.from_sample(min_sample(bqm), bqm), # energy: -1 State.from_sample(max_sample(bqm), bqm), # energy: +1 ) best = ArgMin().run(states).result() self.assertEqual(best.samples.first.energy, -1)
def test_basic(self): bqm = dimod.BinaryQuadraticModel({}, {'ab': 1, 'bc': -1, 'ca': 1}, 0, dimod.SPIN) state = State.from_sample(min_sample(bqm), bqm) antistate = State.from_sample(max_sample(bqm), bqm) result = GreedyPathMerge().run(States(state, antistate)).result() self.assertEqual(result.samples.first.energy, -3.0)
def test_from_samples(self): s1 = [0, 1] s2 = {0: 1, 1: 0} bqm = dimod.BinaryQuadraticModel({0: 1, 1: 2}, {}, 0.0, 'BINARY') self.assertEqual(State.from_sample(s1, bqm).samples.first.energy, 2.0) self.assertEqual(State.from_sample(s2, bqm).samples.first.energy, 1.0) self.assertEqual(State.from_samples([s1, s1], bqm).samples.first.energy, 2.0) self.assertEqual(State.from_samples([s2, s2], bqm).samples.first.energy, 1.0) self.assertEqual(State.from_samples([sample_as_dict(s1), s2], bqm).samples.first.energy, 1.0)
def test_custom_fold(self): bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) states = States( State.from_sample(min_sample(bqm), bqm), # energy: -1 State.from_sample(max_sample(bqm), bqm), # energy: +1 ) fold = ArgMin(key=lambda s: -s.samples.first.energy) best = fold.run(states).result() self.assertEqual(best.samples.first.energy, 1)
def test_multiple(self): bqm = dimod.BinaryQuadraticModel({}, {'ab': 1}, 0, dimod.SPIN) states = States(State.from_sample({'a': 1, 'b': -1}, bqm), State.from_sample({'a': -1, 'b': 1}, bqm)) expected = State.from_samples([{'a': 1, 'b': -1}, {'a': -1, 'b': 1}], bqm) state = MergeSamples().run(states).result() self.assertEqual(state, expected)
def test_custom_key(self): """Custom key function works, here best state has the highest energy.""" bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) states = States( State.from_sample(min_sample(bqm), bqm), # energy: -1 State.from_sample(max_sample(bqm), bqm), # energy: +1 ) tracker = TrackMin(key=lambda s: -s.samples.first.energy) for state in states: tracker.run(state).result() self.assertEqual(tracker.best.samples.first.energy, +1)
def test_default_tracking(self): """Best seen state is kept (default: state with sampleset with the lowest energy)""" bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) min_state = State.from_sample(min_sample(bqm), bqm) # energy: -1 max_state = State.from_sample(max_sample(bqm), bqm) # energy: +1 tracker = TrackMin() _ = tracker.run(max_state).result() self.assertEqual(tracker.best.samples.first.energy, +1) _ = tracker.run(min_state).result() self.assertEqual(tracker.best.samples.first.energy, -1) _ = tracker.run(max_state).result() self.assertEqual(tracker.best.samples.first.energy, -1)
def test_aggregation(self): bqm = dimod.BinaryQuadraticModel({}, {'ab': 1}, 0, dimod.SPIN) states = States(State.from_sample({'a': 1, 'b': -1}, bqm), State.from_sample({'a': 1, 'b': -1}, bqm)) expected = State( problem=bqm, samples=dimod.SampleSet.from_samples_bqm( {'a': 1, 'b': -1}, bqm, num_occurrences=[2])) state = MergeSamples(aggregate=True).run(states).result() self.assertEqual(state, expected)
def test_validation(self): bqm1 = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) bqm2 = dimod.BinaryQuadraticModel({'b': 1}, {}, 0, dimod.SPIN) s1 = State.from_sample({'a': +1}, bqm1) s2 = State.from_sample({'b': -1}, bqm2) # two input states required with self.assertRaises(ValueError): inp = States(s1, s1, s1) IsoenergeticClusterMove().run(inp).result() # variables must match with self.assertRaises(ValueError): inp = States(s1, s2) IsoenergeticClusterMove().run(inp).result()
def test_rolling_subproblem_larger_than_rolling_history(self): """In case rolling history too small, just one problem is unrolled.""" # 10 variables, 0 to 9 when ordered by energy increase on flip bqm = dimod.BinaryQuadraticModel({i: i for i in range(10)}, {}, 0.0, 'SPIN') sample = {i: 1 for i in range(10)} # exactly 1 five-variable problems should be produced state = State.from_sample(sample, bqm) eid = EnergyImpactDecomposer(size=5, rolling=True, rolling_history=0.3, silent_rewind=False) states = list(iter(partial(eid.next, state=state), None)) self.assertEqual(len(states), 1) self.assertEqual(len(states[0].subproblem), 5) self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,5))) # works even for subproblems as large as the input problem eid = EnergyImpactDecomposer(size=len(bqm), rolling=True, rolling_history=0.3, silent_rewind=False) states = list(iter(partial(eid.next, state=state), None)) self.assertEqual(len(states), 1) self.assertEqual(len(states[0].subproblem), 10) self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,10))) # but adapt to problem size if subproblem larger than problem eid = EnergyImpactDecomposer(size=11, rolling=True, rolling_history=0.3, silent_rewind=False) states = list(iter(partial(eid.next, state=state), None)) self.assertEqual(len(states), 1) self.assertEqual(len(states[0].subproblem), 10) self.assertEqual(list(dict(states[0].subproblem.linear).values()), list(range(0,10)))
def test_multi_vars(self): """Multiple variables subproblem selection works, without gain limit.""" state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall) eid = EnergyImpactDecomposer(size=3, min_gain=None) nextstate = eid.next(state) self.assertDictEqual(dict(nextstate.subproblem.adj), dict(self.notall.adj))
def test_nontrivial_subproblem(self): """Check multi-variable multi-coupler subproblem creation. A 3by3 square lattice, with a 2x2 lattice subsolver, is the simplest non-trivial test with edges in the decomposed problem. """ problem_dims = (3, 3) #Vertical edges edgelist = [((i, j), (i + 1, j)) for i in range(problem_dims[0] - 1) for j in range(problem_dims[1])] #Horizontal edges edgelist += [((i, j), (i, j + 1)) for i in range(problem_dims[0]) for j in range(problem_dims[1] - 1)] bqm = dimod.BinaryQuadraticModel.from_ising( {}, {edge: 1 for edge in edgelist}) origin_embeddings = [{(i, j): None for i in range(2) for j in range(2)}] state = State.from_sample(min_sample(bqm), bqm, origin_embeddings=origin_embeddings, problem_dims=problem_dims) # Creates one of 3x3 different subsubproblems, some of which are # disconnected, and some connected # Run multiple times to prevent coincidental agreement runnable = SublatticeDecomposer() for _ in range(10): state = runnable.next(state) self.assertEqual(len(state.subproblem.variables), 4) self.assertIn(next(iter(state.subproblem.variables)), bqm.variables) self.assertEqual(len(state.embedding), 4) self.assertIn(next(iter(state.embedding.keys())), bqm.variables)
def test_trivial_subproblem(self): """Check single-variable no-coupler subproblem creation. A 2by2 square lattice (a square), with a single variable subsolver, is the simplest non-trivial test. """ problem_dims = (2, 2) # Vertical edges edgelist = [((i, j), (i + 1, j)) for i in range(problem_dims[0] - 1) for j in range(problem_dims[1])] # Horizontal edges edgelist += [((i, j), (i, j + 1)) for i in range(problem_dims[0]) for j in range(problem_dims[1] - 1)] bqm = dimod.BinaryQuadraticModel.from_ising( {}, {edge: 1 for edge in edgelist}) origin_embeddings = [{(0, 0): [0]}] state = State.from_sample(min_sample(bqm), bqm, origin_embeddings=origin_embeddings, problem_dims=problem_dims) # Creates subproblems located randomly at (0, 0), (0, 1), (1, 0) and # (1, 1). runnable = SublatticeDecomposer() for _ in range(10): state = runnable.next(state) self.assertEqual(len(state.subproblem.variables), 1) self.assertIn(next(iter(state.subproblem.variables)), bqm.variables) self.assertEqual(len(state.embedding), 1) self.assertIn(next(iter(state.embedding.keys())), bqm.variables)
def test_pfs_on_sequential_eid_calls_over_disconnected_problem_graph(self): # problem graph has two components, each one is 4-node cycle graph edges = {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1, 'ef': 1, 'fg': 1, 'gh': 1, 'he': 1} biases = dict(zip(string.ascii_letters, range(8, 0, -1))) bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN') sample = {i: -1 for i in bqm.variables} state = State.from_sample(sample, bqm) eid = EnergyImpactDecomposer(size=3, traversal='pfs', rolling=True, rolling_history=1.0, silent_rewind=False) states = list(iter(partial(eid.next, state=state), None)) # energy impact list is: [a..h], so of the 3 subproblems generated, # the middle one is disconnected with one var from first group and two # variables from the second # pfs is seeded with `a` and connected nodes in order of energy are picked self.assertEqual(set(states[0].subproblem.variables), set('abc')) # `d` left from the first component, and the seed for the next # subproblem is the next highest in energy `e`. # unlike in bfs, the order of `e`'s neighbors is well defined self.assertEqual(set(states[1].subproblem.variables), set('def')) # the second component is exhausted in search for 3 variable subproblem self.assertEqual(set(states[2].subproblem.variables), set('gh'))
def test_bfs_on_sequential_eid_calls_over_disconnected_problem_graph(self): # problem graph has two components, each one is 4-node cycle graph edges = {'ab': 1, 'bc': 1, 'cd': 1, 'da': 1, 'ef': 1, 'fg': 1, 'gh': 1, 'he': 1} biases = dict(zip(string.ascii_letters, range(8, 0, -1))) bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN') sample = {i: -1 for i in bqm.variables} state = State.from_sample(sample, bqm) eid = EnergyImpactDecomposer(size=3, traversal='bfs', rolling=True, rolling_history=1.0, silent_rewind=False) states = list(iter(partial(eid.next, state=state), None)) # energy impact list is: [a..h], so of the 3 subproblems generated, # the middle one is disconnected with one var from first group and two # variables from the second # `c` has higher energy, but it's not connected to `a`, so `d` is picked self.assertEqual(set(states[0].subproblem.variables), set('abd')) # `c` is picked from the first component, and the seed for the next # subproblem is `e`. however, the order of `e`'s neighbors is not defined, # so if we need to pick just one, it could be `f` or `h` # (note: PFS has a defined order of neighbors) self.assertTrue(set('cefh').difference(states[1].subproblem.variables).issubset('fh')) self.assertEqual(len(states[1].subproblem.variables), 3) # the second component is exhausted in search for 3 variable subproblem third = set(states[2].subproblem.variables) self.assertTrue(third == set('gh') or third == set('gf'))
def run(problems, solver_factories): results = OrderedDict() # reuse the cloud client qpu = EmbeddingComposite(DWaveSampler()) for problem in problems: results[problem] = OrderedDict() with open(problem) as fp: bqm = dimod.BinaryQuadraticModel.from_coo(fp) for name, factory in solver_factories: case = '{!r} with {!r}'.format(problem, name) try: solver = factory(qpu=qpu) init_state = State.from_sample(min_sample(bqm), bqm) with tictoc(case) as timer: solution = solver.run(init_state).result() except Exception as exc: print("FAILED {case}: {exc!r}".format(**locals())) results[problem][name] = repr(exc) else: print("case={case!r}" " energy={solution.samples.first.energy!r}," " wallclock={timer.dt!r}".format(**locals())) results[problem][name] = dict( energy=solution.samples.first.energy, wallclock=timer.dt)
def test_no_vars(self): """Failure due to no sub vars available.""" state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall) eid = EnergyImpactDecomposer(max_size=3, min_gain=5.0) with self.assertRaises(ValueError): nextstate = eid.next(state)
def test_pfs_on_impactful_far_subproblem(self): # problem graph has two components, each one is 4-node cycle graph # variable flip energy gains order variables: a, h..b edges = { 'ab': 1, 'bc': 1, 'cd': 1, 'da': 1, 'ef': 1, 'fg': 1, 'gh': 1, 'he': 1, 'de': 0 } biases = dict(zip(string.ascii_letters, range(8))) biases['a'] += 10 bqm = dimod.BinaryQuadraticModel(biases, edges, 0.0, 'SPIN') sample = {i: -1 for i in bqm.variables} state = State.from_sample(sample, bqm) eid = EnergyImpactDecomposer(size=5, traversal='pfs') result = eid.run(state).result() # move towards second cluster and pick the highest energy variables from there self.assertEqual(set(result.subproblem.variables), set('adehg'))
def test_next_on_different_sized_constraints(self): bqm = dimod.BinaryQuadraticModel.empty(dimod.SPIN) variables = list('abcdefg') fixed_variables = list('abc') size = 3 constraints = [] # Set BQM and constraints of varying lengths for triplet in itertools.combinations(variables, size): for u, v in itertools.combinations(triplet, 2): bqm.add_interaction(u, v, -1) non_fixed_variables = set(triplet) - set(fixed_variables) constraints.append(non_fixed_variables) for fixed_variable in fixed_variables: bqm.fix_variable(fixed_variable, 1) # Get new state rcd = RandomConstraintDecomposer(size, constraints) state = State.from_sample(min_sample(bqm), bqm) newstate = rcd.run(state).result() self.assertIn('subproblem', newstate) self.assertTrue(len(newstate.subproblem) <= size) # correct size
def test_energy_threshold_termination(self): class ExactSolver(Runnable): def next(self, state): return state.updated( samples=dimod.ExactSolver().sample(state.problem)) bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, dimod.SPIN) state = State.from_sample({'a': 1}, bqm) w = LoopUntilNoImprovement( ExactSolver(), key=operator.attrgetter('samples.first.energy'), terminate=partial(operator.ge, -1)) s = w.run(state).result() self.assertEqual(s.samples.first.energy, -1) w = LoopUntilNoImprovement(ExactSolver(), key='samples.first.energy', terminate=partial(operator.ge, -1)) s = w.run(state).result() self.assertEqual(s.samples.first.energy, -1) w = LoopUntilNoImprovement(ExactSolver(), terminate=partial(operator.ge, -1)) s = w.run(state).result() self.assertEqual(s.samples.first.energy, -1)
def test_no_vars(self): """Failure due to no sub vars available.""" state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall) eid = EnergyImpactDecomposer(size=3, min_gain=5.0) nextstate = eid.next(state) self.assertEqual(len(nextstate.subproblem), 0)
def test_adaptive_vars(self): """Multiple variables subproblem selection works, with gain limit.""" state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall) eid = EnergyImpactDecomposer(size=3, min_gain=2.0) nextstate = eid.next(state) self.assertDictEqual(dict(nextstate.subproblem.linear), {'c': 2}) self.assertDictEqual(dict(nextstate.subproblem.quadratic), {})
def test_one_var(self): """First-variable selection works.""" state = State.from_sample({'a': 1, 'b': 1, 'c': -1}, self.notall) eid = EnergyImpactDecomposer(size=1, min_gain=0) nextstate = eid.next(state) self.assertDictEqual(dict(nextstate.subproblem.linear), {'c': 2}) self.assertDictEqual(dict(nextstate.subproblem.quadratic), {})
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_single(self): bqm = dimod.BinaryQuadraticModel({}, {'ab': 1}, 0, dimod.SPIN) states = States(State.from_sample({'a': 1, 'b': -1}, bqm)) state = MergeSamples().run(states).result() self.assertEqual(state, states[0])
def test_energy_correctness(self): bqm = dimod.BinaryQuadraticModel.from_ising({'a': -1}, {}) init = State.from_sample({'a': -1}, bqm) # should be flipped new = RoofDualityDecomposer().run(init).result() self.assertEqual( new.samples.record.energy, bqm.energies((new.samples.record.sample, new.samples.variables)))
def test_simple(self): "Two output states created for two input samples, in correct order." bqm = dimod.BQM.from_ising({}, {'ab': 1}) inp = State.from_samples([{'a': 1, 'b': 1}, {'a': -1, 'b': 1}], bqm) exp = States(State.from_sample({ 'a': 1, 'b': 1 }, bqm), State.from_sample({ 'a': -1, 'b': 1 }, bqm)) out = ExplodeSamples().run(inp).result() self.assertEqual(out, exp)
def test_allfixed(self): bqm = dimod.BinaryQuadraticModel.from_ising({'a': -1}, {}) init = State.from_sample({'a': -1}, bqm) # should be flipped new = RoofDualityDecomposer().run(init).result() self.assertEqual(new.problem, bqm) self.assertEqual(new.subproblem, dimod.BinaryQuadraticModel.from_ising({}, {}, -1)) self.assertEqual(new.samples.record.sample, [1])
def test_greedy_problem_sampler_interface(self): bqm = dimod.BinaryQuadraticModel({'a': 1}, {}, 0, 'SPIN') workflow = GreedyProblemSampler(num_reads=10) init = State.from_sample({'a': 1}, bqm) result = workflow.run(init).result() self.assertEqual(result.samples.first.energy, -1) self.assertEqual(len(result.samples), 10)
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)