def test_as_spin(self): # add_sample with no energy specified, but Q given Q = {(0, 0): -1, (0, 1): 1, (1, 1): -1} sample0 = {0: 0, 1: 1} sample1 = {0: 1, 1: 1} sample2 = {0: 0, 1: 0} h, J, offset = qubo_to_ising(Q) response = self.response_factory() response.add_samples_from([sample0, sample1, sample2], [ qubo_energy(Q, sample0), qubo_energy(Q, sample1), qubo_energy(Q, sample2) ]) response.add_sample(sample0, qubo_energy(Q, sample0)) spin_response = response.as_spin(-1 * offset) for sample, energy in spin_response.items(): self.assertEqual(ising_energy(h, J, sample), energy) spin_response = response.as_spin(-1 * offset, data_copy=True) data_ids = {id(data) for __, data in response.samples(data=True)} for __, data in spin_response.samples(data=True): self.assertNotIn(id(data), data_ids)
def test_path3_weighted(self): G = nx.path_graph(3) G.nodes[1]['weight'] = 2.1 Q = indep.maximum_weighted_independent_set_qubo(G, weight='weight') self.assertLess(dimod.qubo_energy({0: 0, 1: 1, 2: 0}, Q), dimod.qubo_energy({0: 1, 1: 0, 2: 1}, Q))
def test_all_samples(self): """Check that every sample is included and has the correct energy.""" n = 4 # create a qubo Q = {(v, v): (v % 3) for v in range(n)} Q[(0, n - 1)] = 1.3 Q[(3, n - 2)] = -.26666666 response = self.sampler.sample_qubo(Q) # print(response) self.assertEqual(len(response), 2**n, "incorrect number of samples returned") sample_tuples = set() for sample in response.samples(): stpl = tuple(sample[v] for v in range(n)) sample_tuples.add(stpl) for tpl in itertools.product((0, 1), repeat=n): self.assertIn(tpl, sample_tuples) # let's also double check the energy for sample, energy in response.data(['sample', 'energy']): self.assertAlmostEqual(energy, dimod.qubo_energy(sample, Q))
def sample_qubo(self, Q, **kwargs): for key in kwargs: if key not in self.properties['parameters']: raise ValueError result = { 'num_variables': 2048, 'format': 'qp', 'num_occurrences': [1], 'active_variables': list(range(2048)), 'solutions': [[random.choice((0, 1)) for __ in range(2048)]], 'timing': { 'total_real_time': 11511, 'anneal_time_per_run': 20, 'post_processing_overhead_time': 2042, 'qpu_sampling_time': 164, 'readout_time_per_run': 123, 'qpu_delay_time_per_sample': 21, 'qpu_anneal_time_per_sample': 20, 'total_post_processing_time': 2042, 'qpu_programming_time': 8740, 'run_time_chip': 164, 'qpu_access_time': 11511, 'qpu_readout_time_per_sample': 123 }, 'occurrences': [1] } result['samples'] = result['solutions'] result['energies'] = [ dimod.qubo_energy(sample, Q) for sample in result['samples'] ] future = Future() future.set_result(result) return future
def test_all_samples(self): """Check that every sample is included and that they all have the correct energy.""" n = 10 # create a qubo Q = {(v, v): (v % 3) for v in range(n)} Q[(0, n - 1)] = 1.3 Q[(3, n - 2)] = -.26666666 response = self.sampler.sample_qubo(Q) self.assertEqual(len(response), 2**n, "incorrect number of samples returned") sample_tuples = set() for sample in response.samples(): stpl = tuple(sample[v] for v in range(n)) sample_tuples.add(stpl) for tpl in itertools.product((0, 1), repeat=n): self.assertIn(tpl, sample_tuples) # let's also double check the enegy for sample, energy in response.items(): self.assertTrue(abs(energy - qubo_energy(Q, sample)) < .000001)
def test_as_binary(self): h = {0: 0, 1: 0} J = {(0, 1): 1} sample0 = {0: -1, 1: 1} sample1 = {0: 1, 1: -1} sample2 = {0: 1, 1: 1} Q, offset = ising_to_qubo(h, J) response = self.response_factory() response.add_samples_from([sample0, sample1, sample2], [ ising_energy(h, J, sample0), ising_energy(h, J, sample1), ising_energy(h, J, sample2) ]) bin_response = response.as_binary(-1 * offset) for sample, energy in bin_response.items(): self.assertEqual(qubo_energy(Q, sample), energy) bin_response = response.as_binary(-1 * offset, data_copy=True) data_ids = {id(data) for __, data in response.samples(data=True)} for __, data in bin_response.samples(data=True): self.assertNotIn(id(data), data_ids)
def test_basic(self): sampler = self.sampler h = {0: -.5, 1: 0, 2: 1, 3: -.5} J = {(0, 2): -1, (1, 2): -1, (0, 3): .5, (1, 3): -1} response0 = sampler.sample_ising(h, J, num_samples=10) for sample, energy in response0.items(): self.assertEqual(ising_energy(h, J, sample), energy) # make sure we actully got back 100 samples self.assertEqual(len(response0), 10) response2 = sampler.sample_structured_ising(h, J, num_samples=10) self.assertEqual(len(response2), 10) for sample, energy in response2.items(): self.assertEqual(ising_energy(h, J, sample), energy) Q = {(0, 0): 0, (1, 1): 0, (0, 1): -1} response4 = sampler.sample_qubo(Q, num_samples=10) self.assertEqual(len(response4), 10) for sample, energy in response4.items(): self.assertEqual(qubo_energy(Q, sample), energy) response6 = sampler.sample_structured_qubo(Q, num_samples=10) self.assertEqual(len(response6), 10)
def test_sample_qubo(self, sampler, Q): sampleset = sampler.sample_qubo(Q) self.assertEqual(set(sampleset.variables), set().union(*Q)) self.assertIs(sampleset.vartype, dimod.BINARY) for sample, en in sampleset.data(['sample', 'energy']): self.assertAlmostEqual(dimod.qubo_energy(sample, Q), en)
def check_binary_response(self, response, Q): variables = set().union(*Q) for sample, energy in response.items(): for v in variables: self.assertIn(v, sample) self.assertLessEqual(abs(dimod.qubo_energy(Q, sample) - energy), 10**-5)
def test_typical(self): # AND gate h = {0: -.5, 1: 0, 2: 1, 3: -.5} J = {(0, 2): -1, (1, 2): -1, (0, 3): .5, (1, 3): -1} Q, __ = ising_to_qubo(h, J) en0 = min(qubo_energy({ 0: 0, 1: 0, 2: 0, 3: 0 }, Q), qubo_energy({ 0: 0, 1: 0, 2: 0, 3: 1 }, Q)) en1 = min(qubo_energy({ 0: 1, 1: 0, 2: 0, 3: 0 }, Q), qubo_energy({ 0: 1, 1: 0, 2: 0, 3: 1 }, Q)) en2 = min(qubo_energy({ 0: 0, 1: 1, 2: 0, 3: 0 }, Q), qubo_energy({ 0: 0, 1: 1, 2: 0, 3: 1 }, Q)) en3 = min(qubo_energy({ 0: 1, 1: 1, 2: 1, 3: 0 }, Q), qubo_energy({ 0: 1, 1: 1, 2: 1, 3: 1 }, Q)) self.assertEqual(en0, en1) self.assertEqual(en0, en2) self.assertEqual(en0, en3)
def add_sample(self, sample, energy=None, data={}, Q=None): """Loads a sample and associated energy into the response. Args: sample (dict): A sample as would be returned by a discrete model solver. Should be a dict of the form {var: value, ...}. The values should be spin-valued, that is -1 or 1. energy (float/int, optional): The energy associated with the given sample. data (dict, optional): A dict containing any additional data about the sample. Default empty. Q (dict): Defines a Quadratic Unconstrained Binary Optimization problem that can be used to calculate the energy associated with `sample`. Notes: Solutions are stored in order of energy, lowest first. Raises: TypeError: If `sample` is not a dict. TypeError: If `energy` is not an int or float. TypeError: If `data` is not a dict. ValueError: If any of the values in `sample` are not -1 or 1. TypeError: If energy is not provided, Q must be. Examples: >>> sample = {0: 1, 1: 0, 2: 0} >>> energy = -1 >>> response = BinaryResponse() >>> response.add_sample(sample, energy) >>> list(response.samples()) [{0: 1, 1: 0, 2: 0}] >>> response.add_sample(sample, Q={(0, 0): -1, (1, 1): 0, (2, 2): 0}) >>> list(response.items()) [({0: 1, 1: 0, 2: 0}, -1), ({0: 1, 1: 0, 2: 0}, -1)] >>> response.add_sample(sample, data={'n': 10}, Q={(0, 0): -1, (1, 1): 0, (2, 2): 0}) """ # check that the sample is sp]n-valued if any(val not in (0, 1) for val in itervalues(sample)): raise ValueError( 'given sample is not binary. Values should be 0 or 1') # if energy is not provided, but Q is, then we can calculate # the energy for the sample. if energy is None: if Q is None: raise TypeError("most provide 'energy' or 'Q'") energy = qubo_energy(Q, sample) TemplateResponse.add_sample(self, sample, energy, data)
def test_energy(self): h = {v: v for v in range(0, 100, 2)} h.update({v: -(1 / v) for v in range(1, 100, 2)}) J = {(u, v): 2 * (u / 3) + v ** .5 for (u, v) in itertools.combinations(range(100), 2)} spin_sample = {v: 1 if v % 2 else -1 for v in h} bin_sample = {v: 1 if v % 2 else 0 for v in h} Q, off = ising_to_qubo(h, J) ising_en = ising_energy(spin_sample, h, J) qubo_en = qubo_energy(bin_sample, Q) self.assertAlmostEqual(ising_en, qubo_en + off)
def test_spin_transform_composition_basic(self): sampler = self.sampler # let's get a problem that we know the answer to h = {v: .1 for v in range(10)} J = {(u, v): -1. for (u, v) in itertools.combinations(h, 2)} response = sampler.sample_ising(h, J, orig_h=h) # lowest energy sample should be all -1 sample = next(iter(response)) self.assertTrue(all(s == -1 for s in sample.values())) # also energy should still be preserved for sample, energy in response.items(): self.assertLessEqual( abs(dimod.ising_energy(h, J, sample) - energy), 10**-5) for __, data in response.samples(data=True): self.assertIn('spin_reversal_variables', data) Q, __ = dimod.ising_to_qubo(h, J) response = sampler.sample_qubo(Q) # lowest energy sample should be all 0 sample = next(iter(response)) self.assertTrue(all(s == 0 for s in sample.values())) # also energy should still be preserved for sample, energy in response.items(): self.assertLessEqual(abs(dimod.qubo_energy(Q, sample) - energy), 10**-5) for __, data in response.samples(data=True): self.assertIn('spin_reversal_variables', data) response = sampler.sample_ising(h, J, orig_h=h) # lowest energy sample should be all -1 sample = next(iter(response)) self.assertTrue(all(s == -1 for s in sample.values())) # also energy should still be preserved for sample, energy in response.items(): self.assertLessEqual( abs(dimod.ising_energy(h, J, sample) - energy), 10**-5) for __, data in response.samples(data=True): self.assertIn('spin_reversal_variables', data)
def test__vertex_one_color_qubo(self): G = dnx.chimera_graph(2, 2, 4) counter = itertools.count() x_vars = {v: {0: next(counter), 1: next(counter)} for v in G} # get the QUBO Q = _vertex_one_color_qubo(x_vars) # assign each variable a single color sample = {} for v in G: sample[x_vars[v][0]] = 1 sample[x_vars[v][1]] = 0 self.assertEqual(qubo_energy(sample, Q), -1 * len(G))
def test_energy(self): h = {v: v for v in range(0, 100, 2)} h.update({v: -(1 / v) for v in range(1, 100, 2)}) J = {(u, v): 2 * (u / 3) + v**.5 for (u, v) in itertools.combinations(range(100), 2)} spin_sample = {v: 1 if v % 2 else -1 for v in h} bin_sample = {v: 1 if v % 2 else 0 for v in h} Q, off = ising_to_qubo(h, J) ising_en = ising_energy(h, J, spin_sample) qubo_en = qubo_energy(Q, bin_sample) self.assertLessEqual(abs(-ising_en + qubo_en + off), 10**-5)
def test_sample_qubo(self): Q = {(0, 0): 0.0, (1, 1): 0.0, (2, 2): 0.0} Q.update({(0, 1): -1.0, (1, 2): 1.0, (0, 2): 1.0}) response = dimod.ExactPolySolver().sample_hubo(Q) # every possible conbination should be present self.assertEqual(len(response), 2**3) self.assertEqual(response.record.sample.shape, (2**3, 3)) # confirm vartype self.assertIs(response.vartype, dimod.BINARY) # check their energies for sample, energy in response.data(['sample', 'energy']): self.assertAlmostEqual(energy, dimod.qubo_energy(sample, Q))
def test_sample_qubo(self): sampler = EmbeddingComposite(MockDWaveSampler()) Q = {(0, 0): .1, (0, 4): -.8, (4, 4): 1} response = sampler.sample_qubo(Q) # nothing failed and we got at least one response back self.assertGreaterEqual(len(response), 1) for sample in response.samples(): for u, v in Q: self.assertIn(v, sample) self.assertIn(u, sample) for sample, energy in response.data(['sample', 'energy']): self.assertAlmostEqual(dimod.qubo_energy(sample, Q), energy)
def test_as_binary(self): h = {0: 0, 1: 0} J = {(0, 1): 1} sample0 = {0: -1, 1: 1} sample1 = {0: 1, 1: -1} sample2 = {0: 1, 1: 1} Q, offset = ising_to_qubo(h, J) response = self.response_factory() response.add_samples_from([sample0, sample1, sample2], h=h, J=J) bin_response = response.as_binary(-1 * offset) for sample, energy in bin_response.items(): self.assertEqual(qubo_energy(Q, sample), energy)
def test_sample_qubo(self): sampler = TilingComposite(MockDWaveSampler(), 2, 2) Q = {(u, v): random.uniform(-1, 1) for u, v in sampler.structure.edgelist} Q.update({(node, node): random.uniform(-1, 1) for node in sampler.structure.nodelist}) response = sampler.sample_qubo(Q) # nothing failed and we got at least one response back per tile self.assertGreaterEqual(len(response), len(sampler.embeddings)) for sample in response.samples(): for u, v in Q: self.assertIn(v, sample) self.assertIn(u, sample) for sample, energy in response.data(['sample', 'energy']): self.assertAlmostEqual(dimod.qubo_energy(sample, Q), energy)
def test_start_from_spin(self): Q = {(0, 0): 4, (0, 3): 5, (0, 5): 4, (1, 1): 5, (1, 6): 1, (1, 7): -2, (1, 9): -3, (3, 0): -2, (3, 1): 2, (4, 5): 4, (4, 8): 2, (4, 9): -1, (5, 1): 2, (5, 6): -5, (5, 8): -4, (6, 0): 1, (6, 5): 2, (6, 6): -4, (6, 7): -2, (7, 0): -2, (7, 5): -3, (7, 6): -5, (7, 7): -3, (7, 8): 1, (8, 0): 2, (8, 5): 1, (9, 7): -3} qoff = 1.3 h, J, ioff = qubo_to_ising(Q, qoff) bin_sample = {} ising_sample = {} for v in h: bin_sample[v] = 0 ising_sample[v] = -1 self.assertAlmostEqual(ising_energy(ising_sample, h, J, ioff), qubo_energy(bin_sample, Q, qoff))
def test_start_from_binary(self): h = {i: v for i, v in enumerate([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4])} j = {(0, 5): 2, (0, 8): 4, (1, 4): -5, (1, 7): 1, (2, 0): 5, (2, 1): 4, (3, 0): -1, (3, 6): -3, (3, 8): 3, (4, 0): 2, (4, 7): 3, (4, 9): 3, (5, 1): 3, (6, 5): -4, (6, 7): -4, (7, 1): -4, (7, 8): 3, (8, 2): -4, (8, 3): -3, (8, 6): -5, (8, 7): -4, (9, 0): 4, (9, 1): -1, (9, 4): -5, (9, 7): 3} ioff = 1.7 q, qoff = ising_to_qubo(h, j, ioff) bin_sample = {} ising_sample = {} for v in h: bin_sample[v] = 1 ising_sample[v] = 1 self.assertAlmostEqual(ising_energy(ising_sample, h, j, ioff), qubo_energy(bin_sample, q, qoff))
def test_dimod_basic_qubo(self): n_variables = 50 # all 0s Q = {(v, v): 1 for v in range(n_variables)} response = qbs.QBSolv().sample_qubo(Q, seed=10) sample = next(iter(response)) for v in sample: self.assertEqual(sample[v], 0) # all 1s Q = {(u, v): -1 for u, v in itertools.combinations(range(n_variables), 2)} response = qbs.QBSolv().sample_qubo(Q, seed=167) sample = next(iter(response)) for v in sample: self.assertEqual(sample[v], 1) for sample, energy in response.data(['sample', 'energy']): self.assertEqual(dimod.qubo_energy(sample, Q), energy)
def test_as_binary(self): h = {0: 0, 1: 0} J = {(0, 1): 1} sample0 = {0: -1, 1: 1} sample1 = {0: 1, 1: -1} sample2 = {0: 1, 1: 1} Q, offset = dimod.ising_to_qubo(h, J) response = self.response_factory() response.add_samples_from([sample0, sample1, sample2], h=h, J=J) bin_response = response.as_binary(-1 * offset) for sample, energy in bin_response.data(keys=['sample', 'energy']): self.assertEqual(dimod.qubo_energy(sample, Q), energy) bin_response = response.as_binary(-1 * offset) data_ids = {id(data) for __, data in response.samples(data=True)} for __, data in bin_response.samples(data=True): self.assertNotIn(id(data), data_ids)
def test_basic(self): sampler = self.sampler h = {0: -.5, 1: 0, 2: 1, 3: -.5} J = {(0, 2): -1, (1, 2): -1, (0, 3): .5, (1, 3): -1} response0 = sampler.sample_ising(h, J, num_reads=10) for sample, energy in response0.data(['sample', 'energy']): self.assertEqual(dimod.ising_energy(sample, h, J), energy) # make sure we actully got back 100 samples self.assertEqual(len(response0), 10) Q = {(0, 0): 0, (1, 1): 0, (0, 1): -1} response4 = sampler.sample_qubo(Q, num_reads=10) self.assertEqual(len(response4), 10) for sample, energy in response4.data(['sample', 'energy']): self.assertEqual(dimod.qubo_energy(sample, Q), energy)
def test_as_spin_array(self): response = self.response_factory() # set up a BQM and some samples Q = {(0, 0): -1, (0, 1): 1, (1, 1): -1} h, J, offset = dimod.qubo_to_ising(Q) samples = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 0], [1, 1, 1]], dtype=int) energies = np.array([dimod.qubo_energy(row, Q) for row in samples]) response.add_samples_from_array(samples, energies) # cast to spin spin_response = response.as_spin(-offset) # check that the energies are correcct for sample, energy in spin_response.items(): self.assertEqual(dimod.ising_energy(sample, h, J), energy) # make a new spin response spin_response = response.as_spin(offset) data_ids = {id(data) for __, data in response.samples(data=True)} for __, data in spin_response.samples(data=True): self.assertNotIn(id(data), data_ids)
def test__vertex_different_colors_qubo(self): # Chimera tile (can be 2-colored) G = dnx.chimera_graph(1, 1, 4) counter = itertools.count() x_vars = {v: {0: next(counter), 1: next(counter)} for v in G} # get the QUBO Q = _vertex_different_colors_qubo(G, x_vars) # this thing should have energy 0 when each node is a different color bicolor = {v: 0 for v in range(4)} bicolor.update({v: 1 for v in range(4, 8)}) # make the sample from the bicolor sample = {} for v in G: if bicolor[v] == 0: sample[x_vars[v][0]] = 1 sample[x_vars[v][1]] = 0 else: sample[x_vars[v][0]] = 0 sample[x_vars[v][1]] = 1 self.assertEqual(qubo_energy(sample, Q), 0)
def test_trivial(self): en = qubo_energy({}, {}) self.assertEqual(en, 0)
def test__maximal_matching_qubo(self): G = nx.complete_graph(5) B = 1 # magnitude arg for _maximal_matching_qubo edge_mapping = {edge: idx for idx, edge in enumerate(G.edges())} edge_mapping.update({(e1, e0): idx for (e0, e1), idx in edge_mapping.items()}) inv_edge_mapping = {idx: edge for edge, idx in edge_mapping.items()} Q = _maximal_matching_qubo(G, edge_mapping, magnitude=B) # now for each combination of edges, we check that if the combination # is a maximal matching, it has energy magnitude * |edges| ground_energy = -1. * B * len(G.edges()) infeasible_gap = float('inf') for edge_vars in powerset(set(edge_mapping.values())): # get the matching from the variables potential_matching = {inv_edge_mapping[v] for v in edge_vars} # get the sample from the edge_vars sample = {v: 0 for v in edge_mapping.values()} for v in edge_vars: sample[v] = 1 if dnx.is_maximal_matching(G, potential_matching): self.assertEqual(qubo_energy(Q, sample), ground_energy) elif not dnx.is_matching(potential_matching): # for now we don't care about these, they should be covered by the _matching_qubo # part of the QUBO function pass else: en = qubo_energy(Q, sample) gap = en - ground_energy if gap < infeasible_gap: infeasible_gap = gap self.assertLessEqual(B, infeasible_gap) # # Another graph, Chimera tile this time # G = dnx.chimera_graph(1, 2, 2) B = 1 # magnitude arg for _maximal_matching_qubo edge_mapping = {edge: idx for idx, edge in enumerate(G.edges())} edge_mapping.update({(e1, e0): idx for (e0, e1), idx in edge_mapping.items()}) inv_edge_mapping = {idx: edge for edge, idx in edge_mapping.items()} Q = _maximal_matching_qubo(G, edge_mapping, magnitude=B) # now for each combination of edges, we check that if the combination # is a maximal matching, it has energy magnitude * |edges| ground_energy = -1. * B * len(G.edges()) infeasible_gap = float('inf') for edge_vars in powerset(set(edge_mapping.values())): # get the matching from the variables potential_matching = {inv_edge_mapping[v] for v in edge_vars} # get the sample from the edge_vars sample = {v: 0 for v in edge_mapping.values()} for v in edge_vars: sample[v] = 1 if dnx.is_maximal_matching(G, potential_matching): # print potential_matching, qubo_energy(Q, sample) self.assertLess(abs(qubo_energy(Q, sample) - ground_energy), 10**-8) elif not dnx.is_matching(potential_matching): # for now we don't care about these, they should be covered by the _matching_qubo # part of the QUBO function pass else: en = qubo_energy(Q, sample) gap = en - ground_energy if gap < infeasible_gap: infeasible_gap = gap self.assertLessEqual(B - infeasible_gap, 10**-8)
def test_min_maximal_matching_bug1(self): G = nx.Graph() G.add_nodes_from(range(7)) G.add_edges_from([(0, 2), (0, 3), (0, 5), (0, 6), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (4, 6), (5, 6)]) delta = max(G.degree(node) for node in G) # maximum degree A = 1 # magnitude arg for _matching_qubo B = .75 * A / (delta - 2.) # magnitude arg for _maximal_matching_qubo C = .5 * B edge_mapping = _edge_mapping(G) inv_edge_mapping = {idx: edge for edge, idx in edge_mapping.items()} Qmm = _maximal_matching_qubo(G, edge_mapping, magnitude=B) Qm = _matching_qubo(G, edge_mapping, magnitude=A) Qmmm = {(v, v): C for v in edge_mapping.values()} Q = Qmm.copy() for edge, bias in Qm.items(): Q[edge] += bias for edge, bias in Qmmm.items(): Q[edge] += bias # now for each combination of edges, we check that if the combination # is a maximal matching, and if so that is has ground energy, else # there is an infeasible gap ground_energy = -1. * B * len(G.edges()) # from maximal matching infeasible_gap = float('inf') for edge_vars in powerset(set(edge_mapping.values())): # get the matching from the variables potential_matching = {inv_edge_mapping[v] for v in edge_vars} # get the sample from the edge_vars sample = {v: 0 for v in edge_mapping.values()} for v in edge_vars: sample[v] = 1 en_matching = qubo_energy(Qm, sample) en_maximal = qubo_energy(Qmm, sample) en_minimal = qubo_energy(Qmmm, sample) en = qubo_energy(Q, sample) self.assertLess(abs(en_matching + en_maximal + en_minimal - en), 10**-8) if dnx.is_maximal_matching(G, potential_matching): # if the sample is a maximal matching, then let's check each qubo # and combined together self.assertEqual(en_matching, 0.0) # matching self.assertLess(abs(en_maximal - ground_energy), 10**-8) elif dnx.is_matching(potential_matching): # in this case we expect the energy contribution of Qm to be 0 self.assertEqual(en_matching, 0.0) # matching # but there should be a gap to Qmm, because the matching is not maximal self.assertGreater(en_maximal, ground_energy) gap = en_matching + en_maximal + en_minimal - ground_energy if gap < infeasible_gap: infeasible_gap = gap else: # ok, these are not even matching self.assertGreater(en_matching, 0) # so matching energy should be > 0 self.assertGreater(gap, 0) gap = en_matching + en_maximal + en_minimal - ground_energy if gap < infeasible_gap: infeasible_gap = gap self.assertGreater(infeasible_gap, 0) # finally let's test it using the function matching = dnx.min_maximal_matching(G, ExactSolver()) self.assertTrue(dnx.is_maximal_matching(G, matching))
def test_maximal_matching_combined_qubo_bug1(self): # a graph that was not working G = nx.Graph() G.add_nodes_from([0, 1, 2, 3, 4, 5, 6, 7]) G.add_edges_from([(0, 1), (0, 3), (1, 3), (1, 4), (1, 6), (1, 7), (2, 3), (2, 5), (2, 7), (3, 5), (3, 6), (4, 5), (4, 6), (4, 7), (5, 7)]) delta = max(G.degree(node) for node in G) # maximum degree A = 1 # magnitude arg for _matching_qubo B = .95 * A / (delta - 2.) # magnitude arg for _maximal_matching_qubo edge_mapping = _edge_mapping(G) inv_edge_mapping = {idx: edge for edge, idx in edge_mapping.items()} Qmm = _maximal_matching_qubo(G, edge_mapping, magnitude=B) # Q is a defaultdict Qm = _matching_qubo(G, edge_mapping, magnitude=A) Q = Qmm.copy() for edge, bias in Qm.items(): Q[edge] += bias Q = dict( Q ) # we are not necessarily sure that the given sampler can handle a defaultdict Qmm = dict(Qmm) # now for each combination of edges, we check that if the combination # is a maximal matching, and if so that is has ground energy, else # there is an infeasible gap ground_energy = -1. * B * len(G.edges()) # from maximal matching infeasible_gap = float('inf') for edge_vars in powerset(set(edge_mapping.values())): # get the matching from the variables potential_matching = {inv_edge_mapping[v] for v in edge_vars} # get the sample from the edge_vars sample = {v: 0 for v in edge_mapping.values()} for v in edge_vars: sample[v] = 1 en_matching = qubo_energy(Qm, sample) en_maximal = qubo_energy(Qmm, sample) en = qubo_energy(Q, sample) self.assertLess(abs(en_matching + en_maximal - en), 10**-8) if dnx.is_maximal_matching(G, potential_matching): # if the sample is a maximal matching, then let's check each qubo # and combined together self.assertEqual(en_matching, 0.0) # matching self.assertLess(abs(en_maximal - ground_energy), 10**-8) elif dnx.is_matching(potential_matching): # in this case we expect the energy contribution of Qm to be 0 self.assertEqual(en_matching, 0.0) # matching # but there should be a gap to Qmm, because the matching is not maximal self.assertGreater(en_maximal, ground_energy) gap = en_matching + en_maximal - ground_energy if gap < infeasible_gap: infeasible_gap = gap else: # ok, these are not even matching self.assertGreater(en_matching, 0) # so matching energy should be > 0 self.assertGreater(gap, 0) gap = en_matching + en_maximal - ground_energy if gap < infeasible_gap: infeasible_gap = gap self.assertGreater(infeasible_gap, 0)