def submit_dwave_problem(verbosity, physical, samples, anneal_time, spin_revs, postproc): "Submit a QMI to the D-Wave." # Map abbreviated to full names for postprocessing types. postproc = {"": "", "opt": "optimization", "sample": "sampling"}[postproc] # Submit a QMI to the D-Wave and get back a list of solution vectors. solver_params = dict(chains=physical.embedding, num_reads=samples, annealing_time=anneal_time, num_spin_reversal_transforms=spin_revs, postprocess=postproc) unused_params = dict() while True: # Repeatedly remove parameters the particular solver doesn't like until # it actually works -- or fails for a different reason. try: weight_list = qmasm.dict_to_list(physical.weights) answer = solve_ising(qmasm.solver, weight_list, physical.strengths, **solver_params) break except ValueError as e: # Is there a better way to extract the failing symbol than a regular # expression match? bad_name_match = re.match(r'"(.*?)"', str(e)) if bad_name_match == None: raise e bad_name = bad_name_match.group(1) unused_params[bad_name] = solver_params[bad_name] del solver_params[bad_name] except RuntimeError as e: qmasm.abend(e) if verbosity >= 2: # Output parameters we kept and those we discarded sys.stderr.write("Parameters accepted by the %s solver:\n" % qmasm.solver_name) if len(solver_params) > 0: for k, v in solver_params.items(): sys.stderr.write(" %s = %s\n" % (k, v)) else: sys.stderr.write(" [none]\n") sys.stderr.write("\n") sys.stderr.write("Parameters rejected by the %s solver:\n" % qmasm.solver_name) if len(unused_params) > 0: for k, v in unused_params.items(): sys.stderr.write(" %s = %s\n" % (k, v)) else: sys.stderr.write(" [none]\n") sys.stderr.write("\n") # Tally the occurrences of each solution solutions = answer["solutions"] semifinal_answer = unembed_answer(solutions, physical.embedding, broken_chains="vote") try: num_occurrences = {tuple(k): v for k, v in zip(semifinal_answer, answer["num_occurrences"])} except KeyError: num_occurrences = {tuple(a): 1 for a in semifinal_answer} # Discard solutions with broken pins or broken chains. valid_solns = [s for s in solutions if solution_is_intact(physical, s)] final_answer = unembed_answer(valid_solns, physical.embedding, broken_chains="discard") return answer, final_answer, num_occurrences
def discard_broken_chains(self): "Discard solutions with broken chains. Return the new solutions." # Tally the number of occurrences of each solution, because we lose the # association of solution to tally when we discard solutions. tallies = {tuple(s.soln_spins): s.tally for s in self.solutions} # Re-embed the raw solution, discarding broken chains. raw_solns = self.answer["solutions"] good_solns = unembed_answer(raw_solns, self.problem.embedding, broken_chains="discard", h=self.problem.weights, j=self.problem.strengths) # Convert from a list to a set for rapid lookup. good_soln_set = set() for s in good_solns: good_soln_set.add(tuple(s)) # Filter the solution objects, retaining only those appearing in # good_solns. solutions = [] for s in self.solutions: if tuple(s.soln_spins) in good_soln_set: solutions.append(s) return solutions
def discard_broken_chains(self): "Discard solutions with broken chains." # Tally the number of occurrences of each solution, because we lose the # association of solution to tally when we discard solutions. tallies = {tuple(s.soln_spins): s.tally for s in self.solutions} # Re-embed the raw solution, discarding broken chains. raw_solns = self.answer["solutions"] good_solns = unembed_answer(raw_solns, self.problem.embedding, broken_chains="discard", h=self.problem.weights, j=self.problem.strengths) # Convert from a list to a set for rapid lookup. good_soln_set = set() for s in good_solns: good_soln_set.add(tuple(s)) # Filter the solution objects, retaining only those appearing in # good_solns. solutions = [] for s in self.solutions: if tuple(s.soln_spins) in good_soln_set: solutions.append(s) self.solutions = solutions
def submit_dwave_problem(physical, samples, anneal_time): "Submit a QMI to the D-Wave." # Submit a QMI to the D-Wave and get back a list of solution vectors. solver_params = dict(chains=physical.embedding, num_reads=samples, annealing_time=anneal_time) while True: # Repeatedly remove parameters the particular solver doesn't like until # it actually works -- or fails for a different reason. try: answer = solve_ising(qasm.solver, physical.weights, physical.strengths, **solver_params) break except ValueError as e: # Is there a better way to extract the failing symbol than a regular # expression match? bad_name = re.match(r'"(.*?)"', str(e)) if bad_name == None: raise e del solver_params[bad_name.group(1)] except RuntimeError as e: qasm.abend(e) # Discard solutions with broken pins or broken chains. solutions = answer["solutions"] valid_solns = [s for s in solutions if solution_is_intact(physical, s)] final_answer = unembed_answer(valid_solns, physical.embedding, broken_chains="discard") return answer, final_answer
def test_discard(): embeddings = ({2, 5, 6}, [0], (1, 3)) solutions = [[+1, +1, +1, +1, +3, +1, +1], [+1, +1, -1, +1, +1, -1, +1], [+1, +1, -1, +1, +1, -1, -1], [+1, -1, -1, +1, +1, -1, -1], [+1, -1, -1, -1, +1, -1, -1], [-1, -1, -1, -1, 33, -1, -1]] expected = [[+1, +1, +1], [-1, +1, +1], [-1, +1, -1], [-1, -1, -1]] assert unembed_answer(solutions, embeddings, 'discard') == expected
def sapi_refine_modularity( graph, solver, hardware_size, # max size subproblem ptn_variables, # ptn_variables[node] = 0,1,'free' num_reads, annealing_time, embeddings=False, # if false, get fast embedding ): sub_B_matrix, bias, constant = get_sub_mod_matrix(graph, ptn_variables) n = sub_B_matrix.shape[0] if n > hardware_size: print n, hardware_size raise ValueError('Number free variables exceeds hardware size') coupler = {} # we add negative because we maximize modularity bias = [-i for i in bias] for i in range(n - 1): for j in range(i + 1, n): coupler[(i, j)] = -sub_B_matrix.item((i, j)) coupler[(j, i)] = -sub_B_matrix.item((j, i)) A = get_hardware_adjacency(solver) #print "embedding..." if not embeddings: print 'finding embedding ....' embeddings = find_embedding(coupler, A, verbose=0, fast_embedding=True) (h0, j0, jc, new_emb) = embed_problem(bias, coupler, embeddings, A, clean=True, smear=True) emb_j = j0.copy() emb_j.update(jc) #print "On DWave..." result = solve_ising(solver, h0, emb_j, num_reads=num_reads, annealing_time=annealing_time) #print result #print "On DWave...COMPLETE" energies = result['energies'] #print energies #print result['solutions'] #print min(energies), max(energies) new_answer = unembed_answer(result['solutions'], new_emb, 'minimize_energy', bias, coupler) min_energy = 10**10 best_soln = [] for i, ans in enumerate(new_answer): soln = ans[0:n] assert 3 not in soln en = energies[i] if en < min_energy: #print 'energy', en min_energy = en best_soln = copy.deepcopy(soln) return get_new_partition(best_soln, ptn_variables)
def test_minimize_energy_easy(): embeddings = ({0, 1}, [2], (4, 5, 6)) h = [-1] j = {} solutions = [[-1, -1, +1, 3, -1, -1, -1], [-1, +1, -1, 3, +1, +1, +1]] expected = [[-1, +1, -1], [+1, -1, +1]] assert (unembed_answer(solutions, embeddings, 'minimize_energy', h, j) == expected)
def test_discard_all(): embeddings = [[0, 1]] solutions = [ [-1, +1], [-1, +1], [+1, -1], ] assert unembed_answer(solutions, embeddings, 'discard') == []
def test_vote_ties(): embeddings = [[0, 2], {1, 4}] solutions = [[+1, +1, -1, 3, -1], [-1, +1, +1, 3, -1], [+1, -1, -1, 3, +1], [-1, -1, +1, 3, +1]] assert [ x in (+1, -1) for s in unembed_answer(solutions, embeddings, 'vote') for x in s ]
def embedding_example(): # formulate k_6 structured graph h = [1, 1, 1, 1, 1, 1] J = {(i, j): 1 for i in range(6) for j in range(i)} solver = local_connection.get_solver("c4-sw_optimize") A = get_hardware_adjacency(solver) # find and print embeddings for problem graph embeddings = find_embedding(J, A, verbose=1) print "embeddings are: ", embeddings # embed the problem into solver graph (h0, j0, jc, new_emb) = embed_problem(h, J, embeddings, A) print "embedded problem result:\nj0: ", j0 print "jc: ", jc # find unembedded results for chain strengths -0.5, -1.0, -2.0 for chain_strength in (-0.5, -1.0, -2.0): # set chain strength values jc = dict.fromkeys(jc, chain_strength) # create new J array concatenating j0 with jc emb_j = j0.copy() emb_j.update(jc) # solve embedded problem answer = solve_ising(solver, h0, emb_j, num_reads=10) # unembed and print result of the form: # solution [solution #] # var [var #] : [var value] ([qubit index] : [original qubit value] ...) result = unembed_answer(answer['solutions'], new_emb, broken_chains="minimize_energy", h=h, j=J) print "result for chain strength = ", chain_strength for i, (embsol, sol) in enumerate(zip(answer['solutions'], result)): print "solution", i for j, emb in enumerate(embeddings): print "var %d: %d (" % (j, sol[j]), for k in emb: print "%d:%d" % (k, embsol[k]), print ")"
def __init__(self, answer, problem, all_vars): # Store our arguments. self.answer = answer self.problem = problem self.all_vars = all_vars # Unembed the solutions. Fix rather than discard invalid solutions. raw_solns = answer["solutions"] fixed_solns = unembed_answer(raw_solns, problem.embedding, broken_chains="minimize_energy", h=problem.weights, j=problem.strengths) # Define a mapping of physical to logical qubits. phys2log = {} for lq in range(len(self.problem.embedding)): for pq in self.problem.embedding[lq]: phys2log[pq] = lq # Establish mappings from qubit numbers to symbols. max_num = qmasm.sym_map.max_number() num2syms = [[] for _ in range(max_num + 1)] all_num2syms = [[] for _ in range(max_num + 1)] for s, n in qmasm.sym_map.symbol_number_items(): all_num2syms[n].append(s) if all_vars or "$" not in s: num2syms[n].append(s) # Construct one solution object per solution. self.solutions = [] try: tallies = answer["num_occurrences"] except KeyError: tallies = [1] energies = [ (e + problem.simple_offset + problem.offset) / problem.range_scale for e in answer["energies"] ] for i in range(len(fixed_solns)): self.solutions.append( Solution(problem, num2syms, all_num2syms, phys2log, all_vars, raw_solns[i], fixed_solns[i], tallies[i], energies[i]))
def embedding_example(): # formulate k_6 structured graph h = [1, 1, 1, 1, 1, 1] J = {(i, j): 1 for i in range(6) for j in range(i)} solver = local_connection.get_solver("c4-sw_optimize") A = get_hardware_adjacency(solver) # find and print embeddings for problem graph embeddings = find_embedding(J, A, verbose=1) print "embeddings are: ", embeddings # embed the problem into solver graph (h0, j0, jc, new_emb) = embed_problem(h, J, embeddings, A) print "embedded problem result:\nj0: ", j0 print "jc: ", jc # find unembedded results for chain strengths -0.5, -1.0, -2.0 for chain_strength in (-0.5, -1.0, -2.0): # set chain strength values jc = dict.fromkeys(jc, chain_strength) # create new J array concatenating j0 with jc emb_j = j0.copy() emb_j.update(jc) # solve embedded problem answer = solve_ising(solver, h0, emb_j, num_reads=10) # unembed and print result of the form: # solution [solution #] # var [var #] : [var value] ([qubit index] : [original qubit value] ...) result = unembed_answer(answer['solutions'], new_emb, broken_chains="minimize_energy", h=h, j=J) print "result for chain strength = ", chain_strength for i, (embsol, sol) in enumerate(zip(answer['solutions'], result)): print "solution", i for j, emb in enumerate(embeddings): print "var %d: %d (" % (j, sol[j]), for k in emb: print "%d:%d" % (k, embsol[k]), print ")"
def test_weighted_random(): # this test relies on random behaviour! yikes! # ensure runs and acceptance range are both suitably large runs = 10000 embeddings = [(0, 3), [4, 2, 1, 5, 6, 7, 10]] solutions = [ [+1, -1, -1, +1, -1, -1, -1, -1, 3, 3, -1], [-1, +1, +1, +1, -1, -1, -1, -1, 3, 3, -1], ] totals = [[0, 0], [0, 0]] for _ in xrange(runs): usols = unembed_answer(solutions, embeddings, 'weighted_random') for t, s in zip(totals, usols): t[0] += s[0] t[1] += s[1] assert totals[0][0] == runs assert totals[0][1] == -runs assert -400 < totals[1][0] < 400 assert -4586 < totals[1][1] < -3986
def __init__(self, answer, problem, all_vars): # Store our arguments. self.answer = answer self.problem = problem self.all_vars = all_vars # Unembed the solutions. Fix rather than discard invalid solutions. raw_solns = answer["solutions"] fixed_solns = unembed_answer(raw_solns, problem.embedding, broken_chains="minimize_energy", h=problem.weights, j=problem.strengths) # Define a mapping of physical to logical qubits. phys2log = {} for lq in range(len(self.problem.embedding)): for pq in self.problem.embedding[lq]: phys2log[pq] = lq # Establish mappings from qubit numbers to symbols. max_num = qmasm.sym_map.max_number() num2syms = [[] for _ in range(max_num + 1)] all_num2syms = [[] for _ in range(max_num + 1)] for s, n in qmasm.sym_map.symbol_number_items(): all_num2syms[n].append(s) if all_vars or "$" not in s: num2syms[n].append(s) # Construct one solution object per solution. self.solutions = [] try: tallies = answer["num_occurrences"] except KeyError: tallies = [1] energies = [(e + problem.simple_offset + problem.offset)/problem.range_scale for e in answer["energies"]] for i in range(len(fixed_solns)): self.solutions.append(Solution(problem, num2syms, all_num2syms, phys2log, all_vars, raw_solns[i], fixed_solns[i], tallies[i], energies[i]))
def test_minimize_energy(): embeddings = [(0, 5), (1, 6), (2, 7), (3, 8), (4, 10)] h = [] j = { (0, 1): -1, (0, 2): 2, (0, 3): 2, (0, 4): -1, (2, 1): -1, (1, 3): 2, (3, 1): -1, (1, 4): -1, (2, 3): 1, (4, 2): -1, (2, 4): -1, (3, 4): 1 } solutions = [[-1, -1, -1, -1, -1, -1, +1, +1, +1, 3, +1], [+1, +1, +1, +1, +1, -1, +1, -1, -1, 3, -1], [+1, +1, -1, +1, -1, -1, -1, -1, -1, 3, -1]] expected = [[-1, -1, +1, +1, -1], [+1, +1, +1, -1, +1], [-1, -1, -1, +1, -1]] assert (unembed_answer(solutions, embeddings, 'minimize_energy', h, j) == expected)
def get_async_sols(problems, hs, Js, new_embs, method='vote'): """ Returns solution for async problem. Parameters ---------- problems: list list of problem handles returned by async_solve_ising hs: list of lists local fields Js: list of dicts problem couplings new_embs: list embeddings returned from embed_problem method: str decoding method. Can be 'minimize_energy','vote', or 'discard' Returns ------- """ sols = np.empty(len(hs), dtype=object) for n in range(len(hs)): answer = problems[n].result() sols[n] = np.array(unembed_answer(answer['solutions'], new_embs[n], broken_chains=method, h=hs[n], j=Js[n]), dtype=np.int8) return sols
def anneal(C_i, C_ij, mu, sigma, l, strength_scale, energy_fraction, ngauges, max_excited_states): url = "https://usci.qcc.isi.edu/sapi" token = "your-token" h = np.zeros(len(C_i)) J = {} for i in range(len(C_i)): h_i = -2 * sigma[i] * C_i[i] for j in range(len(C_ij[0])): if j > i: J[(i, j)] = 2 * C_ij[i][j] * sigma[i] * sigma[j] h_i += 2 * (sigma[i] * C_ij[i][j] * mu[j]) h[i] = h_i vals = np.array(J.values()) cutoff = np.percentile(vals, AUGMENT_CUTOFF_PERCENTILE) to_delete = [] for k, v in J.items(): if v < cutoff: to_delete.append(k) for k in to_delete: del J[k] isingpartial = [] if FIXING_VARIABLES: Q, _ = ising_to_qubo(h, J) simple = fix_variables(Q, method='standard') new_Q = simple['new_Q'] print('new length', len(new_Q)) isingpartial = simple['fixed_variables'] if (not FIXING_VARIABLES) or len(new_Q) > 0: cant_connect = True while cant_connect: try: print('about to call remote') conn = dwave_sapi2.remote.RemoteConnection(url, token) solver = conn.get_solver("DW2X") print('called remote', conn) cant_connect = False except IOError: print('Network error, trying again', datetime.datetime.now()) time.sleep(10) cant_connect = True A = get_hardware_adjacency(solver) mapping = [] offset = 0 for i in range(len(C_i)): if i in isingpartial: mapping.append(None) offset += 1 else: mapping.append(i - offset) if FIXING_VARIABLES: new_Q_mapped = {} for (first, second), val in new_Q.items(): new_Q_mapped[(mapping[first], mapping[second])] = val h, J, _ = qubo_to_ising(new_Q_mapped) # run gauges nreads = 200 qaresults = np.zeros((ngauges * nreads, len(h))) for g in range(ngauges): embedded = False for attempt in range(5): a = np.sign(np.random.rand(len(h)) - 0.5) h_gauge = h * a J_gauge = {} for i in range(len(h)): for j in range(len(h)): if (i, j) in J: J_gauge[(i, j)] = J[(i, j)] * a[i] * a[j] embeddings = find_embedding(J.keys(), A) try: (h0, j0, jc, new_emb) = embed_problem(h_gauge, J_gauge, embeddings, A, True, True) embedded = True break except ValueError: # no embedding found print('no embedding found') embedded = False continue if not embedded: continue # adjust chain strength rescale_couplers = strength_scale * max( np.amax(np.abs(np.array(h0))), np.amax(np.abs(np.array(list(j0.values()))))) # print('scaling by', rescale_couplers) for k, v in j0.items(): j0[k] /= strength_scale for i in range(len(h0)): h0[i] /= strength_scale emb_j = j0.copy() emb_j.update(jc) print("Quantum annealing") try_again = True while try_again: try: qaresult = solve_ising(solver, h0, emb_j, num_reads=nreads, annealing_time=a_time, answer_mode='raw') try_again = False except: print('runtime or ioerror, trying again') time.sleep(10) try_again = True print("Quantum done") qaresult = np.array( unembed_answer(qaresult["solutions"], new_emb, 'vote', h_gauge, J_gauge)) qaresult = qaresult * a qaresults[g * nreads:(g + 1) * nreads] = qaresult if FIXING_VARIABLES: j = 0 for i in range(len(C_i)): if i in isingpartial: full_strings[:, i] = 2 * isingpartial[i] - 1 else: full_strings[:, i] = qaresults[:, j] j += 1 else: full_strings = qaresults s = full_strings energies = np.zeros(len(qaresults)) s[np.where(s > 1)] = 1.0 s[np.where(s < -1)] = -1.0 bits = len(s[0]) for i in range(bits): energies += 2 * s[:, i] * (-sigma[i] * C_i[i]) for j in range(bits): if j > i: energies += 2 * s[:, i] * s[:, j] * sigma[i] * sigma[ j] * C_ij[i][j] energies += 2 * s[:, i] * sigma[i] * C_ij[i][j] * mu[j] unique_energies, unique_indices = np.unique(energies, return_index=True) ground_energy = np.amin(unique_energies) # print('ground energy', ground_energy) if ground_energy < 0: threshold_energy = (1 - energy_fraction) * ground_energy else: threshold_energy = (1 + energy_fraction) * ground_energy lowest = np.where(unique_energies < threshold_energy) unique_indices = unique_indices[lowest] if len(unique_indices) > max_excited_states: sorted_indices = np.argsort( energies[unique_indices])[-max_excited_states:] unique_indices = unique_indices[sorted_indices] final_answers = full_strings[unique_indices] print('number of selected excited states', len(final_answers)) return final_answers else: final_answer = [] for i in range(len(C_i)): if i in isingpartial: final_answer.append(2 * isingpartial[i] - 1) final_answer = np.array(final_answer) return np.array([final_answer])
#~ #~ Now, we've broken a few chains. We modified the solution array in place, #~ so as we execute the next section, it will use this broken soltuion. #~ #~ Interpreting embedded solutions #~ ------------------------------- #~ #~ Now, we'll take a look at the interpretation of solutions with broken #~ chains. This is still a matter of active research, but the sapi client #~ provides a few strategies. #~ #~ We'll use the ``minimize_energy`` strategy. The description of the #~ strategies provided by the sapi client, can be found in the documentation #~ for :func:`unembed_answer`. The ``minimize_energy`` strategy considers the #~ chains one by one, and computes the hamiltonian energies associated with #~ setting all spins up and all spins down, across the chain, and picks the #~ spin value for that chain which attains the lower energy before moving on #~ to the next chain. #~ new_answer = unembed_answer(result['solutions'], new_emb, 'minimize_energy', h, J) print "spin | chain spins" for spin, chain in zip(new_answer[0], embedding): if spin == -1: spin = '-' else: spin = '+' spins = ['+' if res0[q] == 1 else '-' for q in chain] print " %s | %s"%(spin, "".join(spins))
def test_trivial(): assert unembed_answer([], []) == [] assert unembed_answer([], [], 'minimize_energy') == [] assert unembed_answer([], [], 'vote') == [] assert unembed_answer([], [], 'discard') == [] assert unembed_answer([], [], 'weighted_random') == []
def dwave(pot, states): if pot['num vars'] > 0: solved = False const = 0 h_ = [] J_ = {} state = [] free_state = [] embedding = [] while not solved: try: #global solver #global adj #if solver == 0: sign_in() #should try to make it so there is a pool of pool_size connections that the various threads can use remote_connection = RemoteConnection(url, token) solver = remote_connection.get_solver(solver_name) adj = list(get_hardware_adjacency(solver)) if 'embedding' in pot: const, h_, j, prob_adj = dwave_prepare(pot) embedding = pot['embedding'] else: # if we're doing a new embedding for each f -> v in state i message, then we'll have frozen a variable # so we need to remap the variables, since otherwise the h will have a 0 for this variable, but the embedding won't consider it map_vars(pot) const, h_, j, prob_adj = dwave_prepare(pot) while len(embedding) == 0: embedding = find_embedding(prob_adj, adj).values() [h, J, chains, embedding] = embed_problem(h_, j, embedding, adj) s = 0.50 h = [a * s for a in h] for k in J: J[k] = J[k] * s for k in chains: if k in J: J[k] += chains[k] else: J[k] = chains[k] # Submit problem #print('submitting problem') submitted_problems = [ async_solve_ising(solver, h, J, num_reads=10000, num_spin_reversal_transforms=5, answer_mode='histogram', auto_scale=True) ] await_completion(submitted_problems, len(submitted_problems), float('180')) res = unembed_answer( submitted_problems[0].result()['solutions'], embedding, 'discard') if len(res) > 0: state = array(res[0]) solved = True except Exception as err: print(err) solved = False #sleep(30) # wait 30 seconds and retry if len(h_) != len(state): print(h_, len(h_)) print(state, len(state)) print(pot) J_, _ = dict_2_mat(j, len(h_)) energy = h_.dot(state) + state.dot(J_.dot(state.transpose())) + const #for v in sorted(free_state): # energy += pot[v]*free_state[v] # state = append(state, free_state[v]) return energy, state else: if 'const' in pot: return pot['const'], states[0] else: return 0, states[0]
def submit_dwave_problem(verbosity, physical, samples, anneal_time, spin_revs, postproc, discard): "Submit a QMI to the D-Wave." # Map abbreviated to full names for postprocessing types. postproc = {"": "", "opt": "optimization", "sample": "sampling"}[postproc] # Determine the annealing time to use. if anneal_time == None: try: # Use the default value. anneal_time = qmasm.solver.properties["default_annealing_time"] except KeyError: try: # If the default value is undefined, use the minimum allowed # value. anneal_time = qmasm.solver.properties["annealing_time_range"][0] except KeyError: # If all else fails, use 20 as a reasonable default. annealing_time = 20 # Submit a QMI to the D-Wave and get back a list of solution vectors. solver_params = dict(chains=physical.embedding, num_reads=samples, annealing_time=anneal_time, num_spin_reversal_transforms=spin_revs, postprocess=postproc) unused_params = dict() while True: # Repeatedly remove parameters the particular solver doesn't like until # it actually works -- or fails for a different reason. try: weight_list = qmasm.dict_to_list(physical.weights) answer = solve_ising(qmasm.solver, weight_list, physical.strengths, **solver_params) break except ValueError as e: # Is there a better way to extract the failing symbol than a regular # expression match? bad_name_match = re.match(r'"(.*?)"', str(e)) if bad_name_match == None: raise e bad_name = bad_name_match.group(1) unused_params[bad_name] = solver_params[bad_name] del solver_params[bad_name] except RuntimeError as e: qmasm.abend(e) if verbosity >= 2: # Output parameters we kept and those we discarded sys.stderr.write("Parameters accepted by the %s solver:\n" % qmasm.solver_name) if len(solver_params) > 0: for k, v in list(solver_params.items()): sys.stderr.write(" %s = %s\n" % (k, v)) else: sys.stderr.write(" [none]\n") sys.stderr.write("\n") sys.stderr.write("Parameters rejected by the %s solver:\n" % qmasm.solver_name) if len(unused_params) > 0: for k, v in list(unused_params.items()): sys.stderr.write(" %s = %s\n" % (k, v)) else: sys.stderr.write(" [none]\n") sys.stderr.write("\n") # Tally the occurrences of each solution solutions = answer["solutions"] semifinal_answer = unembed_answer(solutions, physical.embedding, broken_chains="minimize_energy", h=physical.weights, j=physical.strengths) try: num_occurrences = {tuple(k): v for k, v in zip(semifinal_answer, answer["num_occurrences"])} except KeyError: num_occurrences = {tuple(a): 1 for a in semifinal_answer} # Discard solutions with broken pins or broken chains unless instructed not to. valid_solns = [s for s in solutions if solution_is_intact(physical, s)] num_not_broken = len(valid_solns) if discard in ["yes", "maybe"]: final_answer = unembed_answer(valid_solns, physical.embedding, broken_chains="discard", h=physical.weights, j=physical.strengths) if discard == "no" or (discard == "maybe" and len(final_answer) == 0): final_answer = semifinal_answer return answer, final_answer, num_occurrences, num_not_broken
def submit_dwave_problem(verbosity, physical, samples, anneal_time, spin_revs, postproc, discard): "Submit a QMI to the D-Wave." # Map abbreviated to full names for postprocessing types. postproc = { "none": "", "opt": "optimization", "sample": "sampling" }[postproc] # Determine the annealing time to use. if anneal_time == None: anneal_time = get_default_annealing_time() # Compute a list of the number of samples to take each iteration # and the number of spin reversals to perform. samples_list = compute_sample_counts(samples, anneal_time) spin_rev_list = compute_spin_rev_counts(spin_revs, samples_list) nqmis = len(samples_list) # Number of (non-unique) QMIs to submit # Submit one or more QMIs to the D-Wave. problems = [] for i in range(nqmis): solver_params = dict(chains=physical.embedding, num_reads=samples_list[i], annealing_time=anneal_time, num_spin_reversal_transforms=spin_rev_list[i], postprocess=postproc) unused_params = dict() while True: # Repeatedly remove parameters the particular solver doesn't like # until it actually works -- or fails for a different reason. try: weight_list = qmasm.dict_to_list(physical.weights) p = async_solve_ising(qmasm.solver, weight_list, physical.strengths, **solver_params) problems.append(p) break except ValueError as e: # Is there a better way to extract the failing symbol than a # regular expression match? bad_name_match = re.match(r'"(.*?)"', str(e)) if bad_name_match == None: raise e bad_name = bad_name_match.group(1) unused_params[bad_name] = solver_params[bad_name] del solver_params[bad_name] except RuntimeError as e: qmasm.abend(e) if verbosity >= 2: report_parameters_used(solver_params, unused_params) # Output problem IDs as soon as they become available. if verbosity >= 1: try: while any([ problems[i].status()["problem_id"] == "" for i in range(nqmis) ]): await_completion(problems, nqmis, 1) report_subproblems_submitted(nqmis, problems, samples_list, spin_rev_list) except KeyError: pass # Not all solvers support "problem_id". # Wait for the solver to complete. if verbosity >= 2: sys.stderr.write("Number of subproblems completed:\n\n") cdigits = len(str(nqmis)) # Digits in the number of completed QMIs tdigits = len(str(nqmis * 5)) # Estimate 5 seconds per QMI submission start_time = time.time() done = False while not done: done = await_completion(problems, nqmis, 10) if verbosity >= 2: ncomplete = sum([ problems[i].status()["state"] == "DONE" for i in range(nqmis) ]) sys.stderr.write( " %*d of %d (%3.0f%%) after %*.0f seconds\n" % (cdigits, ncomplete, nqmis, 100.0 * float(ncomplete) / float(nqmis), tdigits, time.time() - start_time)) if verbosity >= 2: sys.stderr.write("\n") answers = [p.result() for p in problems] # Tally the occurrences of each solution answer = merge_answers(answers) solutions = answer["solutions"] semifinal_answer = unembed_answer(solutions, physical.embedding, broken_chains="minimize_energy", h=physical.weights, j=physical.strengths) try: num_occurrences = { tuple(k): v for k, v in zip(semifinal_answer, answer["num_occurrences"]) } except KeyError: num_occurrences = {tuple(a): 1 for a in semifinal_answer} # Discard solutions with broken pins or broken chains unless instructed # not to. valid_solns = [s for s in solutions if solution_is_intact(physical, s)] num_not_broken = len(valid_solns) if discard in ["yes", "maybe"]: final_answer = unembed_answer(valid_solns, physical.embedding, broken_chains="discard", h=physical.weights, j=physical.strengths) if discard == "no" or (discard == "maybe" and len(final_answer) == 0): final_answer = semifinal_answer return answer, final_answer, num_occurrences, num_not_broken
# We are now ready to embed our problem onto the graph: [h, j0, jc, embeddings] = embed_problem(h, J, embedding, adjacency) # j0 contains the original couplings that we defined and jc contains the couplings that enforce the integrity of the chains (they correlate the qubits within the chains). Thus, we need to combine them again into one big J dictionary: J = j0.copy() J.update(jc) # Now, we're ready to solve the embedded problem: params = {"answer_mode": 'histogram', "num_reads": 10000} raw_results = solve_ising(solver, h, J, **params) print 'Lowest energy found: {}'.format(raw_results['energies']) print 'Number of occurences: {}'.format(raw_results['num_occurrences']) unembedded_results = unembed_answer(raw_results['solutions'], embedding, broken_chains='vote') print 'Solution string: {}'.format(unembedded_results) ''' you should get the correct result in all readouts: Lowest energy found: [-5.0] Number of occurences: [10000] Solution string: [[1, -1, -1, 1]] '''
def query_dwave(self, h, J, embedding, samples, temperature, batch_size): """ Queries D-Wave multiple times for solution of the given Ising model, aggregating the unembedded results. """ results = { 'energies': [], 'solutions': [], 'num_occurrences': [], 'timing': [] } num_batches = samples // batch_size for i in range(num_batches): batch_solved = False while not batch_solved: try: self.info("Sampling batch {i}".format(i=i)) batch = solve_ising(self.solver, h, J, answer_mode='histogram', auto_scale=True, num_reads=batch_size, num_spin_reversal_transforms=5, beta=1.0 / float(temperature), postprocess='sampling', chains=embedding) batch_solved = True self.info("Done") except Exception as e: self.verbose(str(e)) self.info("Exception occured, retrying...") # Collect the batch results batch_solutions = unembed_answer(batch['solutions'], embedding, broken_chains='vote') results['solutions'].extend(batch_solutions) results['num_occurrences'].extend(batch['num_occurrences']) # Aggregate the same unembedded answers aggregated = defaultdict(dict) data = zip(results['solutions'], results['num_occurrences']) for result, count in data: key = tuple(result) result_data = aggregated.get(key, {}) # Recompute the average solution energy prior_count = result_data.get('count', 0) result_data['count'] = prior_count + count aggregated[key] = result_data # Return as a sorted list aggregated_list = [(key, value['count']) for key, value in aggregated.items()] return list(sorted(aggregated_list, key=lambda x: x[1]))
def solvequbo(self): # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # EMBEDDING: # gets the hardware adjacency for the solver in use. self.Adjacency = get_hardware_adjacency(self.solver) # gets the embedding for the D-Wave hardware self.Embedding = find_embedding(self.qubo_dict, self.Adjacency) # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # CONVERSIONS AND RESCALING: # convert qubo to ising (self.h, self.J, self.ising_offset) = qubo_to_ising(self.qubo_dict) # Even though auto_scale = TRUE, we are rescaling values # Normalize h and J to be between +/-1 self.h_max = max(map(abs, self.h)) if len(self.J.values()) > 0: j_max = max([abs(x) for x in self.J.values()]) else: j_max = 1 # In [0,1], this scales down J values to be less than jc j_scale = 0.8 # Use the largest large value if self.h_max > j_max: j_max = self.h_max # This is the actual scaling rescale = j_scale / j_max self.h1 = map(lambda x: rescale * x, self.h) if len(self.J.values()) > 0: self.J1 = {key: rescale * val for key, val in self.J.items()} else: self.J1 = self.J # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # EMBEDDING: # gets the hardware adjacency for the solver in use. self.Adjacency = get_hardware_adjacency(self.solver) # gets the embedding for the D-Wave hardware self.Embedding = find_embedding(self.qubo_dict, self.Adjacency) # Embed the rescale values into the hardware graph [self.h0, self.j0, self.jc, self.Embedding ] = embed_problem(self.h1, self.J1, self.Embedding, self.Adjacency, self.clean, self.smear, self.h_range, self.J_range) # embed_problem returns two J's, one for the biases from your problem, one for the chains. self.j0.update(self.jc) # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # SOLVE PROBLEM ON D-WAVE: # generate the embedded solution to the ising problem. self.dwave_return = solve_ising(self.solver, self.h0, self.j0, **self.params) #print("dwave_return") #print(self.dwave_return['solutions']) # the unembedded answer to the ising problem. unembed = np.array( unembed_answer(self.dwave_return['solutions'], self.Embedding, broken_chains="minimize_energy", h=self.h, j=self.J)) #[0] # convert ising string to qubo string ising_ans = [ list(filter(lambda a: a != 3, unembed[i])) for i in range(len(unembed)) ] #print(ising_ans) #print("ISING ANS") # Because the problem is unembedded, the energy will be different for the embedded, and unembedded problem. # ising_energies = dwave_return['energies'] self.h_energy = [ sum(self.h1[v] * val for v, val in enumerate(unembed[i])) for i in range(len(unembed)) ] self.J_energy = [ sum(self.J1[(u, v)] * unembed[i, u] * unembed[i, v] for u, v in self.J1) for i in range(len(unembed)) ] self.ising_energies = np.array(self.h_energy) + np.array(self.J_energy) #print(self.h_energy) #print(self.J_energy) #print(self.ising_energies) #print("ENERGIES") # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # CONVERT ANSWER WITH ENERGY TO QUBO FORM: # Rescale and add back in the ising_offset and another constant self.dwave_energies = self.ising_energies / rescale + self.ising_offset #[map(lambda x: (x / rescale + self.ising_offset), self.ising_energies[i]) for i in range(len(self.ising_energies))] # QUBO RESULTS: self.qubo_ans = ( np.array(ising_ans) + 1 ) / 2 #[map(lambda x: (x + 1) / 2, ising_ans[i]) for i in range(len(ising_ans))]
def runDW_batch(h, J, embedding, stop_point=0.25, num_reads=1000, coupling_init=1.0, coupling_increment=0.1, min_solver_calls=1, max_solver_calls=1000, method='vote', last=True, num_gauges=1, solver_name='NASA', returnProblems=True): ''' Submits an instance to DW as a batch. Note that when used, sometimes the solutions are markedly different than when use runDW (no batch). Generally using run_DW() seems to be a better idea Parameters ----- h : list of lists, with each list is a list of fields J : a list of dictionary, where keys are a tuple corresponding to the coupling. Should be the same length as h. embedding : a list of lists. Can use DW sapi to generate stop_point :float, default: 0.25. Stop increasing coupling strength when returns at least this fraction of solutions are unbroken. num_reads: int, default: 1000. The number of reads. coupling_init: float, default: 1.0. The initial value of coupling, the value of the ferromagnetic coupling between physical qubits. If number of unbroken of solutions is not at least stop_point, then the magnitude of coupling is incremented by coupling_increment. Note however, that the though we specify coupling_init as positive, the coupling is negative. For example, Suppose coupling_init=1.0, coupling_increment (defined below) is 0.1, and stop_point = 0.25. The initial physical ferromagnetic coupling strength will be -1.0. If stop_point isn't reached, coupling is incremented by 0.1, or in other words, the new chain strength is -1.1. coupling is incremented by coupling_increment until stop_point is reached. coupling_increment: float, default: 0.1. Increment of coupling strength, min_solver_calls: int, default: 1. The minimum number of solver calls. max_solver_calls: int, default: 1000. The maximum number of solver calls. method: str, 'minimize_energy', 'vote', or 'discard', default: 'minimize_energy' How to deal with broken chains. 'minimize_energy' uses the energy minimization decoding. 'vote' uses majority vote decoding. 'discard' discard broken chains. last: bool, default: True If True, return the last num_reads solutions. If False, return the first num_reads solutions. num_gauges: int, default: 1 Number of gauge transformations. solver_name: str, 'NASA', 'ISI', or 'DW', default: 'NASA' Which solver to use. 'NASA' uses NASA's DW2000Q. 'ISI' uses ISI's DW2X. 'DW' uses DW's DW2000Q. returnProblems: bool Determines what it returns. If True, return problems, new_emb. If False return solutions only Returns ------- if returnProblems is True, returns problems, new_emb (to be used with get_async_sols) problems: list list of problems from async_solve_ising new_emb: list list of embeddings returned from embed_problem if returnProblems is False, returns solutions sols: np array Array of solutions ''' meths = ['discard', 'vote', 'minimize_energy'] assert (method in meths) if solver_name == 'NASA': url = 'https://qfe.nas.nasa.gov/sapi' token = 'NASA-870f7ee194d029923ad8f9cd063de357ba53b838' remote_connection = RemoteConnection(url, token) solver = remote_connection.get_solver('C16') elif solver_name == 'ISI': url = 'https://usci.qcc.isi.edu/sapi' token = 'QUCB-089028555cb44b4f3da34cd4c6dd4a73ec859bc8' remote_connection = RemoteConnection(url, token) solver = remote_connection.get_solver('DW2X') elif solver_name == 'DW': url = 'https://cloud.dwavesys.com/sapi' token = 'usc-171bafd63a1b07635fd696db283ad4c28b820d14' remote_connection = RemoteConnection(url, token) solver = remote_connection.get_solver('DW_2000Q_2_1') else: NameError('Unrecognized solver name') A = get_hardware_adjacency(solver) h0 = [] j0 = [] jc = [] new_emb = [] for n in range(len(h)): (h0t, j0t, jct, new_embt) = embed_problem(h[0], J[0], embedding, A) maxjh = max(max(np.abs(h0t)), max(np.abs(j0t.values()))) h0t = [el / maxjh for el in h0t] j0t = {ij: v / maxjh for ij, v in zip(j0t.keys(), j0t.values())} h0.append(h0t) j0.append(j0t) jc.append(jct) new_emb.append(new_embt) ncalls = 0 sols = np.empty(len(h0), dtype=object) if isinstance(coupling_init, list): l = coupling_init else: l = [coupling_init] * len(h) print np.unique(l) kwargs = { 'num_reads': num_reads, 'num_spin_reversal_transforms': num_gauges, 'answer_mode': 'raw' } problem = [] for n in range(len(h0)): jct = dict.fromkeys(jc[n], -l[n]) emb_j = j0[n].copy() emb_j.update(jct) if solver_name == 'ISI': _check_wait() problem.append(async_solve_ising(solver, h0[n], emb_j, **kwargs)) await_completion(problem, len(h), 50000) if returnProblems: return problem, new_emb for n in range(len(h0)): answer = problem[n].result() sols[n] = np.array(unembed_answer(answer['solutions'], new_emb[n], broken_chains=method, h=h[n], j=J[n]), dtype=np.int8) # return problem,new_emb return np.array(sols)
def test_vote_noties(): embeddings = [(1, 2), [3, 5, 4]] solutions = [(3, +1, +1, -1, -1, -1), (3, -1, -1, -1, +1, +1), (3, -1, -1, +1, +1, -1), (3, +1, +1, -1, +1, -1)] expected = [[+1, -1], [-1, +1], [-1, +1], [+1, -1]] assert unembed_answer(solutions, embeddings, 'vote') == expected
def runDW(h, J, embedding, stop_point=0.25, num_reads=1000, coupling_init=1.0, coupling_increment=0.1, min_solver_calls=1, max_solver_calls=1000, method='vote', last=True, num_gauges=1, solver_name='NASA', annealing_time=20): ''' Submits an instance to DW. Parameters ----- h : list, a list of fields J : a dictionary, where keys are a tuple corresponding to the coupling embedding : a list of lists. Can use DW sapi to generate stop_point :float, default: 0.25. Stop increasing coupling strength when returns at least this fraction of solutions are unbroken. num_reads: int, default: 1000. The number of reads. coupling_init: float, default: 1.0. The initial value of coupling, the value of the ferromagnetic coupling between physical qubits. If number of unbroken of solutions is not at least stop_point, then the magnitude of coupling is incremented by coupling_increment. Note however, that the though we specify coupling_init as positive, the coupling is negative. For example, Suppose coupling_init=1.0, coupling_increment (defined below) is 0.1, and stop_point = 0.25. The initial physical ferromagnetic coupling strength will be -1.0. If stop_point isn't reached, coupling is incremented by 0.1, or in other words, the new chain strength is -1.1. coupling is incremented by coupling_increment until stop_point is reached. coupling_increment: float, default: 0.1. Increment of coupling strength, min_solver_calls: int, default: 1. The minimum number of solver calls. max_solver_calls: int, default: 1000. The maximum number of solver calls. method: str, 'minimize_energy', 'vote', or 'discard', default: 'minimize_energy' How to deal with broken chains. 'minimize_energy' uses the energy minimization decoding. 'vote' uses majority vote decoding. 'discard' discard broken chains. last: bool, default: True If True, return the last num_reads solutions. If False, return the first num_reads solutions. num_gauges: int, default: 1 Number of gauge transformations. solver_name: str, 'NASA', 'ISI', or 'DW', default: 'NASA' Which solver to use. 'NASA' uses NASA's DW2000Q. 'ISI' uses ISI's DW2X. 'DW' uses DW's DW2000Q. Returns ------- A tuple of sols, c, ratio sols: numpy ndarray, shape = [num_reads, num_spins] Solutions where each row is a set of spins (num_spins dependent on solver) c: float The final value of the coupling strength used ratio: float The final fraction of unbroken solutions returned at coupling_strength c ''' meths = ['discard', 'vote', 'minimize_energy'] assert (method in meths) solver = connectSolver(solver_name) A = get_hardware_adjacency(solver) # embed problem (h0, j0, jc, new_emb) = embed_problem(h, J, embedding, A) # scale problem maxjh = max(max(np.abs(h0)), max(np.abs(j0.values()))) h0 = [el / maxjh for el in h0] j0 = {ij: v / maxjh for ij, v in zip(j0.keys(), j0.values())} ratio = 0 sols = [] ncalls = 0 l = coupling_init print coupling_init jc = dict.fromkeys(jc, -l) emb_j = j0.copy() emb_j.update(jc) kwargs = { 'num_reads': num_reads, 'num_spin_reversal_transforms': num_gauges, 'answer_mode': 'raw', 'annealing_time': annealing_time } # iteratively increase ferromagentic strength until returns a certain ratio # of solutions where there are no broken chains while ratio <= stop_point and ncalls < max_solver_calls: jc = dict.fromkeys(jc, -l) emb_j = j0.copy() emb_j.update(jc) if solver_name == 'ISI': _check_wait() problem = async_solve_ising(solver, h0, emb_j, **kwargs) await_completion([problem], 1, 50000) answer = problem.result() result = unembed_answer(answer['solutions'], new_emb, broken_chains='discard', h=h, j=J) sols += result nSols = len(result) l = l + coupling_increment ratio = nSols / float(len(answer['solutions'])) ncalls = ncalls + 1 print ratio # Don't remember why do this. Maybe for good measure if method == 'discard': nAdd = int(1 / ratio) + 1 else: nAdd = 1 l = l - coupling_increment for n in range(nAdd): if solver_name == 'ISI': _check_wait() problem = async_solve_ising(solver, h0, emb_j, **kwargs) await_completion([problem], 1, 50000) answer = problem.result() result = unembed_answer(answer['solutions'], new_emb, broken_chains=method, h=h, j=J) sols += result if len(sols) < num_reads: # try one more time problem = async_solve_ising(solver, h0, emb_j, **kwargs) await_completion([problem], 1, 50000) answer = problem.result() result = unembed_answer(answer['solutions'], new_emb, broken_chains=method, h=h, j=J) sols += result if last: if len(sols) >= num_reads: sols = sols[-num_reads:] else: print 'WARNING! DID NOT COLLECT ENOUGH READS...continuing' else: sols = sols[:num_reads] return (np.array(sols, dtype=np.int8), l, ratio)
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
#~ #~ Now, we've broken a few chains. We modified the solution array in place, #~ so as we execute the next section, it will use this broken soltuion. #~ #~ Interpreting embedded solutions #~ ------------------------------- #~ #~ Now, we'll take a look at the interpretation of solutions with broken #~ chains. This is still a matter of active research, but the sapi client #~ provides a few strategies. #~ #~ We'll use the ``minimize_energy`` strategy. The description of the #~ strategies provided by the sapi client, can be found in the documentation #~ for :func:`unembed_answer`. The ``minimize_energy`` strategy considers the #~ chains one by one, and computes the hamiltonian energies associated with #~ setting all spins up and all spins down, across the chain, and picks the #~ spin value for that chain which attains the lower energy before moving on #~ to the next chain. #~ new_answer = unembed_answer(result['solutions'], new_emb, 'minimize_energy', h, J) print "spin | chain spins" for spin, chain in zip(new_answer[0], flat_embedding): if spin == -1: spin = '-' else: spin = '+' spins = ['+' if res0[q] == 1 else '-' for q in chain] print " %s | %s" % (spin, "".join(spins))
emb = embedding.find_embedding(S, A, verbose=1) print emb Q = {(0, 0): 1, (1, 1): 1, (2, 2): 1, (0, 1): 1, (0, 2): -2, (1, 2): -2} (h, j, ising_offset) = util.qubo_to_ising(Q) print "h:", h print "j:", j print "ising_offset:", ising_offset (h0, j0, jc, new_emb) = embedding.embed_problem(h, j, emb, A) print "h0:", h0 print "j0:", j0 print "jc:", jc print "new_emb:", new_emb emb_j = j0.copy() emb_j.update(jc) print "emb_j:", emb_j ans = core.solve_ising(solver, h0, emb_j, num_reads=1000) print ans # for x in ans['solutions']: # for y in new_emb: # print (x[y[0]]+1)/2 # print '----' res = embedding.unembed_answer(ans['solutions'], new_emb, 'discard', h, j) print(np.array(res) + 1) / 2