def convert_to_qubo(self): """Transform an Ising problem into a QUBO problem. Return the new QUBO problem.""" if self.qubo: raise TypeError("Can convert only Ising problems to QUBO problems") new_obj = copy.deepcopy(self) qmatrix, _ = ising_to_qubo(self.weights, self.strengths) new_obj.weights = [0] * len(self.weights) for (q1, q2), wt in qmatrix.items(): if q1 == q2: new_obj.weights[q1] = wt new_obj.strengths = {(q1, q2): wt for (q1, q2), wt in qmatrix.items() if q1 != q2} return new_obj
def convert_to_qubo(self): """Transform an Ising problem into a QUBO problem. Return the new QUBO problem.""" if self.qubo: raise TypeError("Can convert only Ising problems to QUBO problems") new_obj = copy.deepcopy(self) qmatrix, _ = ising_to_qubo(qmasm.dict_to_list(self.weights), self.strengths) new_obj.weights = defaultdict(lambda: 0.0, {q1: wt for (q1, q2), wt in list(qmatrix.items()) if q1 == q2}) new_obj.strengths = defaultdict(lambda: 0.0, {(q1, q2): wt for (q1, q2), wt in list(qmatrix.items()) if q1 != q2}) new_obj.qubo = True return new_obj
def convert_to_qubo(self): """Transform an Ising problem into a QUBO problem. Return the new QUBO problem.""" if self.qubo: raise TypeError("Can convert only Ising problems to QUBO problems") new_obj = copy.deepcopy(self) qmatrix, qoffset = ising_to_qubo(qmasm.dict_to_list(self.weights), self.strengths) new_obj.offset = qoffset new_obj.weights = defaultdict(lambda: 0.0, {q1: wt for (q1, q2), wt in qmatrix.items() if q1 == q2}) new_obj.strengths = qmasm.canonicalize_strengths({(q1, q2): wt for (q1, q2), wt in qmatrix.items() if q1 != q2}) new_obj.qubo = True return new_obj
def simplify_problem(logical, verbosity): """Try to find spins that can be removed from the problem because their value is known a priori.""" # SAPI's fix_variables function works only on QUBOs so we have to convert. # We directly use SAPI's ising_to_qubo function instead of our own # convert_to_qubo because the QUBO has to be in matrix form. hs = qmasm.dict_to_list(logical.weights) Js = logical.strengths Q, qubo_offset = ising_to_qubo(hs, Js) # Simplify the problem if possible. simple = fix_variables(Q, method="optimized") fixed_vars = simple["fixed_variables"] # At high verbosity levels, list all of the known symbols and their value. if verbosity >= 2: # Map each logical qubit to one or more symbols. num2syms = [[] for _ in range(len(qmasm.sym2num))] max_sym_name_len = 7 for q, n in qmasm.sym2num.items(): num2syms[n].append(q) max_sym_name_len = max(max_sym_name_len, len(repr(num2syms[n])) - 1) # Output a table of know values sys.stderr.write( "Elided qubits whose low-energy value can be determined a priori:\n\n" ) if len(fixed_vars) > 0: sys.stderr.write(" Logical %-*s Value\n" % (max_sym_name_len, "Name(s)")) sys.stderr.write(" ------- %s -----\n" % ("-" * max_sym_name_len)) truval = {0: "False", +1: "True"} for q, b in sorted(fixed_vars.items()): if num2syms[q] == []: continue name_list = " ".join(sorted(num2syms[q])) sys.stderr.write(" %7d %-*s %-s\n" % (q, max_sym_name_len, name_list, truval[b])) sys.stderr.write("\n") # Return the original problem if no qubits could be elided. if verbosity >= 2: sys.stderr.write(" %6d logical qubits before elision\n" % (qmasm.next_sym_num + 1)) if len(fixed_vars) == 0: if verbosity >= 2: sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.next_sym_num + 1)) return logical # Construct a simplified problem, renumbering so as to compact qubit # numbers. new_obj = copy.deepcopy(logical) new_obj.known_values = { s: 2 * fixed_vars[n] - 1 for s, n in qmasm.sym2num.items() if n in fixed_vars } new_obj.simple_offset = simple["offset"] hs, Js, ising_offset = qubo_to_ising(simple["new_Q"]) qubits_used = set([i for i in range(len(hs)) if hs[i] != 0.0]) for q1, q2 in Js.keys(): qubits_used.add(q1) qubits_used.add(q2) qmap = dict(zip(sorted(qubits_used), range(len(qubits_used)))) new_obj.chains = {(qmap[q1], qmap[q2]): None for q1, q2 in new_obj.chains.keys() if q1 in qmap and q2 in qmap} new_obj.weights = defaultdict( lambda: 0.0, {qmap[i]: hs[i] for i in range(len(hs)) if hs[i] != 0.0}) new_obj.strengths = qmasm.canonicalize_strengths({ (qmap[q1], qmap[q2]): wt for (q1, q2), wt in Js.items() }) new_obj.pinned = [(qmap[q], b) for q, b in new_obj.pinned if q in qmap] qmasm.sym2num = {s: qmap[q] for s, q in qmasm.sym2num.items() if q in qmap} try: qmasm.next_sym_num = max(qmasm.sym2num.values()) except ValueError: qmasm.next_sym_num = -1 if verbosity >= 2: sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.next_sym_num + 1)) return new_obj
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])
def simplify_problem(logical, verbosity): """Try to find spins that can be removed from the problem because their value is known a priori.""" # SAPI's fix_variables function works only on QUBOs so we have to convert. # We directly use SAPI's ising_to_qubo function instead of our own # convert_to_qubo because the QUBO has to be in matrix form. hs = qmasm.dict_to_list(logical.weights) Js = logical.strengths Q, qubo_offset = ising_to_qubo(hs, Js) # Simplify the problem if possible. simple = fix_variables(Q, method="standard") fixed_vars = simple["fixed_variables"] if verbosity >= 2: # Also determine if we could get rid of more qubits if we care about # only *a* solution rather than *all* solutions. alt_simple = fix_variables(Q, method="optimized") all_gone = len(alt_simple["new_Q"]) == 0 # At high verbosity levels, list all of the known symbols and their value. if verbosity >= 2: # Map each logical qubit to one or more symbols. num2syms = [[] for _ in range(qmasm.sym_map.max_number() + 1)] max_sym_name_len = 7 for q, n in qmasm.sym_map.symbol_number_items(): num2syms[n].append(q) max_sym_name_len = max(max_sym_name_len, len(repr(num2syms[n])) - 1) # Output a table of know values sys.stderr.write("Elided qubits whose low-energy value can be determined a priori:\n\n") if len(fixed_vars) > 0: sys.stderr.write(" Logical %-*s Value\n" % (max_sym_name_len, "Name(s)")) sys.stderr.write(" ------- %s -----\n" % ("-" * max_sym_name_len)) truval = {0: "False", +1: "True"} for q, b in sorted(fixed_vars.items()): try: syms = qmasm.sym_map.to_symbols(q) except KeyError: continue name_list = " ".join(sorted(syms)) sys.stderr.write(" %7d %-*s %-s\n" % (q, max_sym_name_len, name_list, truval[b])) sys.stderr.write("\n") # Return the original problem if no qubits could be elided. if verbosity >= 2: sys.stderr.write(" %6d logical qubits before elision\n" % (qmasm.sym_map.max_number() + 1)) if len(fixed_vars) == 0: if verbosity >= 2: sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.sym_map.max_number() + 1)) if all_gone: sys.stderr.write(" Note: A complete solution can be found classically using roof duality and strongly connected components.\n\n") return logical # Construct a simplified problem, renumbering so as to compact qubit # numbers. new_obj = copy.deepcopy(logical) new_obj.known_values = {s: 2*fixed_vars[n] - 1 for s, n in qmasm.sym_map.symbol_number_items() if n in fixed_vars} new_obj.simple_offset = simple["offset"] hs, Js, ising_offset = qubo_to_ising(simple["new_Q"]) qubits_used = set([i for i in range(len(hs)) if hs[i] != 0.0]) for q1, q2 in Js.keys(): qubits_used.add(q1) qubits_used.add(q2) qmap = dict(zip(sorted(qubits_used), range(len(qubits_used)))) new_obj.chains = set([(qmap[q1], qmap[q2]) for q1, q2 in new_obj.chains if q1 in qmap and q2 in qmap]) new_obj.weights = defaultdict(lambda: 0.0, {qmap[i]: hs[i] for i in range(len(hs)) if hs[i] != 0.0}) new_obj.strengths = qmasm.canonicalize_strengths({(qmap[q1], qmap[q2]): wt for (q1, q2), wt in Js.items()}) new_obj.pinned = [(qmap[q], b) for q, b in new_obj.pinned if q in qmap] qmasm.sym_map.overwrite_with({s: qmap[q] for s, q in qmasm.sym_map.symbol_number_items() if q in qmap}) if verbosity >= 2: # Report the number of logical qubits that remain, but compute the # number that could be removed if only a single solution were required. sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.sym_map.max_number() + 1)) if all_gone: sys.stderr.write(" Note: A complete solution can be found classically using roof duality and strongly connected components.\n\n") return new_obj
def simplify_problem(logical, verbosity): """Try to find spins that can be removed from the problem because their value is known a priori.""" # SAPI's fix_variables function works only on QUBOs so we have to convert. # We directly use SAPI's ising_to_qubo function instead of our own # convert_to_qubo because the QUBO has to be in matrix form. hs = qmasm.dict_to_list(logical.weights) Js = logical.strengths Q, qubo_offset = ising_to_qubo(hs, Js) # Simplify the problem if possible. simple = fix_variables(Q, method="standard") new_Q = simple["new_Q"] fixed_vars = simple["fixed_variables"] if verbosity >= 2: # Also determine if we could get rid of more qubits if we care about # only *a* solution rather than *all* solutions. alt_simple = fix_variables(Q, method="optimized") all_gone = len(alt_simple["new_Q"]) == 0 # Work around the rare case in which fix_variables drops a variable # entirely, leaving it neither in new_Q nor in fixed_variables. If this # happenes, we explicitly re-add the variable from Q to new_Q and # transitively everything it touches (removing from fixed_vars if a # variable appears there). old_vars = qubo_vars(Q) new_vars = qubo_vars(new_Q) new_vars.update(fixed_vars) missing_vars = sorted(old_vars.difference(new_vars)) while len(missing_vars) > 0: q = missing_vars.pop() for (q1, q2), val in Q.items(): if q1 == q or q2 == q: new_Q[(q1, q2)] = val fixed_vars.pop(q1, None) fixed_vars.pop(q2, None) if q1 == q and q2 > q: missing_vars.append(q2) elif q2 == q and q1 > q: missing_vars.append(q1) # At high verbosity levels, list all of the known symbols and their value. if verbosity >= 2: # Map each logical qubit to one or more symbols. num2syms = [[] for _ in range(qmasm.sym_map.max_number() + 1)] max_sym_name_len = 7 for q, n in qmasm.sym_map.symbol_number_items(): num2syms[n].append(q) max_sym_name_len = max(max_sym_name_len, len(repr(num2syms[n])) - 1) # Output a table of know values sys.stderr.write("Elided qubits whose low-energy value can be determined a priori:\n\n") if len(fixed_vars) > 0: sys.stderr.write(" Logical %-*s Value\n" % (max_sym_name_len, "Name(s)")) sys.stderr.write(" ------- %s -----\n" % ("-" * max_sym_name_len)) truval = {0: "False", +1: "True"} for q, b in sorted(fixed_vars.items()): try: syms = qmasm.sym_map.to_symbols(q) except KeyError: continue name_list = " ".join(sorted(syms)) sys.stderr.write(" %7d %-*s %-s\n" % (q, max_sym_name_len, name_list, truval[b])) sys.stderr.write("\n") # Return the original problem if no qubits could be elided. if verbosity >= 2: sys.stderr.write(" %6d logical qubits before elision\n" % (qmasm.sym_map.max_number() + 1)) if len(fixed_vars) == 0: if verbosity >= 2: sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.sym_map.max_number() + 1)) if all_gone: sys.stderr.write(" Note: A complete solution can be found classically using roof duality and strongly connected components.\n\n") return logical # Construct a simplified problem, renumbering so as to compact qubit # numbers. new_obj = copy.deepcopy(logical) new_obj.known_values = {s: 2*fixed_vars[n] - 1 for s, n in qmasm.sym_map.symbol_number_items() if n in fixed_vars} new_obj.simple_offset = simple["offset"] hs, Js, ising_offset = qubo_to_ising(new_Q) qubits_used = set([i for i in range(len(hs)) if hs[i] != 0.0]) for q1, q2 in Js.keys(): qubits_used.add(q1) qubits_used.add(q2) qmap = dict(zip(sorted(qubits_used), range(len(qubits_used)))) new_obj.chains = set([(qmap[q1], qmap[q2]) for q1, q2 in new_obj.chains if q1 in qmap and q2 in qmap]) new_obj.antichains = set([(qmap[q1], qmap[q2]) for q1, q2 in new_obj.antichains if q1 in qmap and q2 in qmap]) new_obj.weights = defaultdict(lambda: 0.0, {qmap[i]: hs[i] for i in range(len(hs)) if hs[i] != 0.0}) new_obj.strengths = qmasm.canonicalize_strengths({(qmap[q1], qmap[q2]): wt for (q1, q2), wt in Js.items()}) new_obj.pinned = [(qmap[q], b) for q, b in new_obj.pinned if q in qmap] qmasm.sym_map.overwrite_with({s: qmap[q] for s, q in qmasm.sym_map.symbol_number_items() if q in qmap}) if verbosity >= 2: # Report the number of logical qubits that remain, but compute the # number that could be removed if only a single solution were required. sys.stderr.write(" %6d logical qubits after elision\n\n" % (qmasm.sym_map.max_number() + 1)) if qmasm.sym_map.max_number() > -1 and all_gone: sys.stderr.write(" Note: A complete solution can be found classically using roof duality and strongly connected components.\n\n") return new_obj
def test_trivial(): q, offset = ising_to_qubo([], {}) assert q == {} assert offset == 0
def test_typical(): h = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] j = { (0, 0): -5, (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, 6): -5, (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 } q, offset = ising_to_qubo(h, j) norm_q = normalized_matrix(q) assert norm_q == { (0, 0): -42, (0, 2): 20, (0, 3): -4, (0, 4): 8, (0, 5): 8, (0, 8): 16, (0, 9): 16, (1, 1): -4, (1, 2): 16, (1, 4): -20, (1, 5): 12, (1, 7): -12, (1, 9): -4, (2, 2): -16, (2, 8): -16, (3, 3): 4, (3, 6): -12, (4, 4): 2, (4, 7): 12, (4, 9): -8, (5, 5): -2, (5, 6): -16, (6, 6): 34, (6, 7): -16, (6, 8): -20, (7, 7): 8, (7, 8): -4, (7, 9): 12, (8, 8): 18 } assert offset == -8
def test_j_diag(): q, offset = ising_to_qubo([], {(0, 0): 1, (300, 300): 99}) assert q == {} assert offset == 100
def test_no_zeros(): q, offset = ising_to_qubo([0, 0, 0], {(0, 0): 0, (4, 5): 0}) assert q == {} assert offset == 0