def test_discard_with_dimod(self): sample0 = {0: -1, 1: -1, 2: +1} sample1 = {0: +1, 1: -1, 2: +1} samples = [sample0, sample1] # now set up an embedding that works for one sample and not the other embedding = {'a': {0, 1}, 'b': {2}} # load the samples into a dimod response response = dimod.SpinResponse() response.add_samples_from(samples, (0 for __ in samples)) # specify that majority vote should be used source_samples = eutil.unembed_samples( response, embedding, chain_break_method=eutil.discard) # only the first sample should be returned self.assertEqual(len(source_samples), 1) self.assertEqual(source_samples, [{'a': -1, 'b': +1}])
def test_majority_vote_with_dimod(self): sample0 = {0: -1, 1: -1, 2: +1} sample1 = {0: +1, 1: -1, 2: +1} samples = [sample0, sample1] embedding = {'a': {0, 1, 2}} # load the samples into a dimod response response = dimod.SpinResponse() response.add_samples_from(samples, (0 for __ in samples)) # specify that majority vote should be used source_samples = eutil.unembed_samples( response, embedding, chain_break_method=eutil.majority_vote) source0, source1 = source_samples self.assertEqual(source0['a'], -1) self.assertEqual(source1['a'], +1)
def test_typical_dimod_response(self): """unembed should be compatible with the dimod response object""" nodes = range(10) target_samples = [{v: random.choice((-1, 1)) for v in nodes} for __ in range(100)] # load the samples into a dimod response response = dimod.SpinResponse() response.add_samples_from(target_samples, (0 for __ in target_samples)) # embedding is map half the target vars to one source, and the other half to # another embedding = {'a': range(5), 'b': range(5, 10)} # use the response as the target_samples source_samples = eutil.unembed_samples(response, embedding) # not checking correctness of samples, just that they have the correct form self.assertEqual(len(source_samples), 100) for sample in source_samples: self.assertIsInstance(sample, dict) self.assertEqual(set(sample), {'a', 'b'}) # maps to source vars self.assertTrue(all(bias in (-1, 1) for bias in sample.values()))
def sample_ising(self, h, J, embedding_tag=None, **sapi_kwargs): """Embeds the given problem using sapi's find_embedding then invokes the given sampler to solve it. Args: h (dict/list): The linear terms in the Ising problem. If a dict, should be of the form {v: bias, ...} where v is a variable in the Ising problem, and bias is the linear bias associated with v. If a list, should be of the form [bias, ...] where the indices of the biases are the variables in the Ising problem. J (dict): A dictionary of the quadratic terms in the Ising problem. Should be of the form {(u, v): bias} where u, v are variables in the Ising problem and bias is the quadratic bias associated with u, v. embedding_tag: Allows the user to specify a tag for the generated embedding. Useful for when the user wishes to submit multiple problems with the same logical structure. Additional keyword parameters are the same as for SAPI's solve_ising function, see QUBIST documentation. Returns: :class:`dimod.SpinResponse`: The unembedded samples. Examples: >>> sampler = sapi.EmbeddingComposite(sapi.SAPILocalSampler('c4-sw_optimize')) >>> response = sampler.sample_ising({}, {(0, 1): 1, (0, 2): 1, (1, 2): 1}) Using the embedding_tag, the embedding is generated only once. >>> h = {0: .1, 1: 1.3, 2: -1.} >>> J = {(0, 1): 1, (1, 2): 1, (0, 2): 1} >>> sampler = sapi.EmbeddingComposite(sapi.SAPILocalSampler('c4-sw_optimize')) >>> response0 = sampler.sample_ising(h, J, embedding_tag='K3') >>> response1 = sampler.sample_ising(h, J, embedding_tag='K3') """ # get the sampler that is used by the composite sampler = self._child # the ising_index_labels decorator converted the keys of h to be indices 0, n-1 # sapi wants h to be a list, so let's make that conversion, using the keys as # the indices. h_list = [h[v] for v in range(len(h))] # get the structure of the child sampler. The first value are the nodes which # we don't need, the second is the set of edges available. (__, edgeset) = sampler.structure if embedding_tag is None or embedding_tag not in self.cached_embeddings: # we have not previously cached an embedding so we need to determine it # get the adjacency structure of our problem S = set(J) S.update({(v, v) for v in h}) # embed our adjacency structure, S, into the edgeset of the sampler. embeddings = find_embedding(S, edgeset) # sometimes it fails, often because the problem is too large if J and not embeddings: raise Exception('No embedding found') # now it is possible that h_list might include nodes not in embedding, so let's # handle that case here if len(h_list) > len(embeddings): emb_qubits = set().union(*embeddings) while len(h_list) > len(embeddings): for v in sampler.solver.properties['qubits']: if v not in emb_qubits: embeddings.append([v]) emb_qubits.add(v) break if embedding_tag is not None: # save the embedding for posterity self.cached_embeddings[embedding_tag] = embeddings else: # the user has asserted that we can reuse a previously created embedding embeddings = self.cached_embeddings[embedding_tag] # embed the problem h0, j0, jc, new_emb = embed_problem(h_list, J, embeddings, edgeset) # combine jc and j0 emb_j = j0.copy() emb_j.update(jc) # pass the chains we made into the sampler if it wants them if 'chains' in sampler.solver.properties['parameters'] and 'chains' not in sapi_kwargs: sapi_kwargs['chains'] = new_emb # invoke the child sampler emb_response = sampler.sample_ising(h0, emb_j, **sapi_kwargs) # we need the samples back into lists for the unembed_answer function answers = [[sample[i] for i in range(len(sample))] for sample in emb_response] # unemnbed solutions = unembed_answer(answers, new_emb, 'minimize_energy', h_list, J) # and back once again into dicts for dimod... samples = ({v: sample[v] for v in h} for sample in solutions) sample_data = (data for __, data in emb_response.samples(data=True)) response = dimod.SpinResponse() response.add_samples_from(samples, sample_data=sample_data, h=h, J=J) return response