def find_max(constraints, expr, l = None): if l is None: l = logger if type(expr) == int: return expr constraint_strs = [f'{c}' for c in constraints] max_optimize = Optimize() max_optimize.set('timeout', 10000) max_optimize.assert_exprs(*constraints) max_optimize.maximize(expr) status = max_optimize.check() if status != sat: l.warning(f'Unable to find max ({status}) for:\n' + '\n'.join(constraint_strs)) return None max_val = max_optimize.model().eval(expr).as_long() # Make sure it's actually the max, since z3 has a bug # https://github.com/Z3Prover/z3/issues/4670 solver = Solver() solver.set('timeout', 10000) solver.add(constraints + [expr > max_val]) status = solver.check() if status != unsat: l.error(f'Z3 bug\nFind max ({expr}) => {max_val} with status ({status}):\n' + '\n'.join(constraint_strs)) return None return max_val
def main(): bots = [] with open("input.txt") as f: for line in f: bots.append(tuple(map(int, re.findall(r"-?\d+", line)))) x, y, z, r = max(bots, key=lambda b: b[3]) in_range = sum( (abs(x - b[0]) + abs(y - b[1]) + abs(z - b[2]) <= r) for b in bots) print("Part 1:", in_range) x, y, z = Int("x"), Int("y"), Int("z") point = (x, y, z) count = sum(If(z3_dist(b[:3], point) <= b[3], 1, 0) for b in bots) opt = Optimize() opt.maximize(count) opt.minimize(z3_dist(point, (0, 0, 0))) opt.check() model = opt.model() result = model[x].as_long() + model[y].as_long() + model[z].as_long() print("Part 2:", result)
def test(qcirc): #print(qcirc) print("Translate to Z3") circ = QuantumCircuit_to_Circuit(qcirc, melbourne_rels, positions) # Collect Constraints print("Create Constraint Generator") cons = circ.constraints() # New Optimizer s = Optimize() # Add constraints print("Add All constraints") for con in cons: s.add(con) #print(con) # Add reliability objective print("Add Objective") s.maximize(reliability_objective(circ)) # Check and print print("Checking") sat = str(s.check()) print(sat) if sat == "unsat": raise Exception("unsat") print("Extracting Model") m = s.model() # Confirm that an improvement in reliability was made print("Reliability Checking") improved_reliability(m, s, melbourne_rels, qcirc) # Translate to qiskit QuantumCircuit print("Translate to Qiskit") rcirc = model_to_QuantumCircuit(m, circ)
def get_model(constraints, minimize=(), maximize=()): s = Optimize() s.set("timeout", 100000) for constraint in constraints: if type(constraint) == bool and not constraint: raise UnsatError constraints = [ constraint for constraint in constraints if type(constraint) != bool ] for constraint in constraints: s.add(constraint) for e in minimize: s.minimize(e) for e in maximize: s.maximize(e) result = s.check() if result == sat: return s.model() elif result == unknown: logging.debug("Timeout encountered while solving expression using z3") raise UnsatError
def main(args): data = [extract(s.strip()) for s in sys.stdin] data = [(x[3], tuple(x[:-1])) for x in data] m = max(data) in_range = [x for x in data if dist(x[1], m[1]) <= m[0]] print(len(in_range)) x = Int('x') y = Int('y') z = Int('z') orig = (x, y, z) cost = Int('cost') cost_expr = x * 0 for r, pos in data: cost_expr += If(z3_dist(orig, pos) <= r, 1, 0) opt = Optimize() print("let's go") opt.add(cost == cost_expr) opt.maximize(cost) # I didn't do this step in my initial #2 ranking solution but I # suppose you should. # z3 does them lexicographically by default. opt.minimize(z3_dist((0, 0, 0), (x, y, z))) opt.check() model = opt.model() # print(model) pos = (model[x].as_long(), model[y].as_long(), model[z].as_long()) print("position:", pos) print("num in range:", model[cost].as_long()) print("distance:", dist((0, 0, 0), pos))
class Y2021D24(object): _re_inp = re.compile(r'inp ([wxyz])') _re_add = re.compile(r'add ([wxyz]) ([wxyz]|-?\d+)') _re_mul = re.compile(r'mul ([wxyz]) ([wxyz]|-?\d+)') _re_div = re.compile(r'div ([wxyz]) ([wxyz]|-?\d+)') _re_mod = re.compile(r'mod ([wxyz]) ([wxyz]|-?\d+)') _re_eql = re.compile(r'eql ([wxyz]) ([wxyz]|-?\d+)') def __init__(self, file_name): self.solver = Optimize() inputs = [Int(f'model_{i}') for i in range(14)] self.solver.add([i >= 1 for i in inputs]) self.solver.add([i <= 9 for i in inputs]) # Please don't ask me to explain this. There's a common pattern in the input code that treats z like a number # of base 26 and the operations are either right shift or left shift on that number +- some value. self.solver.add(inputs[0] + 6 - 6 == inputs[13]) self.solver.add(inputs[1] + 11 - 6 == inputs[12]) self.solver.add(inputs[2] + 5 - 13 == inputs[11]) self.solver.add(inputs[3] + 6 - 8 == inputs[8]) self.solver.add(inputs[4] + 8 - 1 == inputs[5]) self.solver.add(inputs[6] + 9 - 16 == inputs[7]) self.solver.add(inputs[9] + 13 - 16 == inputs[10]) my_sum = IntVal(0) for index in range(len(inputs)): my_sum = (my_sum * 10) + inputs[index] self.value = Int('value') self.solver.add(my_sum == self.value) def part1(self): self.solver.push() self.solver.maximize(self.value) self.solver.check() result = self.solver.model()[self.value] self.solver.pop() print("Part 1:", result) def part2(self): self.solver.push() self.solver.minimize(self.value) self.solver.check() result = self.solver.model()[self.value] self.solver.pop() print("Part 2:", result)
def optimum_dist(bots): x = Int('x') y = Int('y') z = Int('z') cost_expr = x * 0 for i, j, k, r in bots: cost_expr += If(z3_dist((x, y, z), (i, j, k)) <= r, 1, 0) opt = Optimize() opt.maximize(cost_expr) opt.minimize(z3_dist((0, 0, 0), (x, y, z))) opt.check() model = opt.model() coords = (model[x].as_long(), model[y].as_long(), model[z].as_long()) return dist((0, 0, 0), coords)
def part_2(nanobots: List[Nanobot]) -> int: x, y, z = Ints("x y z") opt = Optimize() bot_cond = [] for i, bot in enumerate(nanobots): cond = Int(f"bot_{i}") bot_cond.append(cond) opt.add(cond == If(z_manhattan(x, y, z, bot.point) <= bot.r, 1, 0)) overlaps = Sum(bot_cond) dist_zero = Int('dist_zero') opt.add(dist_zero == z_manhattan(x, y, z, Point(0, 0, 0))) _ = opt.maximize(overlaps) dist = opt.maximize(dist_zero) opt.check() return dist.upper()
def find_optimal_space(nanobots): (x, y, z) = (Int('x'), Int('y'), Int('z')) in_ranges = [ Int('in_range_{}'.format(i)) for i in range(len(nanobots)) ] range_count = Int('sum') optimiser = Optimize() for i, nanobot in enumerate(nanobots): optimiser.add(in_ranges[i] == If(zabs(x - nanobot.x) + zabs(y - nanobot.y) + zabs(z - nanobot.z) <= nanobot.r, 1, 0)) optimiser.add(range_count == sum(in_ranges)) dist_from_zero = Int('dist') optimiser.add(dist_from_zero == zabs(x) + zabs(y) + zabs(z)) optimiser.maximize(range_count) result = optimiser.minimize(dist_from_zero) optimiser.check() return optimiser.lower(result)
def maximize_distance(bots): o = Optimize() z3_abs = lambda k: If(k >= 0, k, -k) z3_in_ranges = [Int('in_range_of_bot_' + str(i)) for i in xrange(len(bots))] z3_x, z3_y, z3_z = (Int('x'), Int('y'), Int('z')) z3_sum = Int('sum') z3_dist = Int('dist') for i, (x, y, z, r) in enumerate(bots): o.add(z3_in_ranges[i] == If(distance((z3_x, z3_y, z3_z), (x, y, z), z3_abs) <= r, 1, 0)) o.add(z3_sum == sum(z3_in_ranges)) o.add(z3_dist == distance((z3_x, z3_y, z3_z), (0, 0, 0), z3_abs)) h1, h2 = o.maximize(z3_sum), o.minimize(z3_dist) o.check() # o.lower(h1), o.upper(h1) lower, upper = o.lower(h2), o.upper(h2) # o.model()[z3_x], o.model()[z3_y], o.model()[z3_z] if str(lower) != str(upper): raise Exception('lower ({}) != upper ({})'.format(lower, upper)) return (lower, upper)
# for fidelity optimization if objective_name == "fidelity": for n in range(N): z3.add(u[n] == sum([If(space[l] == n, 1, 0) for l in G_1])) z3.add(w[n] == sum([If(pi[m][T - 1] == n, 1, 0) for m in range(M)])) for k in range(K): z3.add(v[k] == sum([If(space[l] == k, 1, 0) for l in G_2])) z3.add(vv[k] == sum([If(sigma[k][t], 1, 0) for t in range(T)])) z3.add(fidelity == sum([v[k] * f_2qbg[k] for k in range(K)]) + sum([S * vv[k] * f_2qbg[k] for k in range(K)]) + sum([w[n] * f_meas[n] for n in range(N)]) + sum([u[n] * f_1qbg[n] for n in range(N)])) z3.maximize(fidelity) # for depth optimization # else: for l in range(L): z3.add(depth >= time[l] + 1) # z3.minimize(depth) if objective_name == "swap": z3.minimize(num_swap) elif objective_name == "depth": ze.minimize(depth) satisfiable = z3.check() if satisfiable == sat: not_solve = False
def solve(self, output_mode: str = None, output_file_name: str = None): """Formulate an SMT, pass it to z3 solver, and output results. CORE OF OLSQ, EDIT WITH CARE. Args: output_mode: "IR" or left to default. output_file_name: a file to store the IR output or qasm. Returns: a list of results depending on output_mode "IR": | list_scheduled_gate_name: name/type of each gate | list_scheduled_gate_qubits: qubit(s) each gate acts on | final_mapping: logical qubit |-> physical qubit in the end | objective_value: depth/#swap/fidelity depending on setting None: a qasm string final_mapping objective_value """ objective_name = self.objective_name device = self.device list_gate_qubits = self.list_gate_qubits count_program_qubit = self.count_program_qubit list_gate_name = self.list_gate_name count_physical_qubit = self.count_physical_qubit list_qubit_edge = self.list_qubit_edge swap_duration = self.swap_duration bound_depth = self.bound_depth # pre-processing count_qubit_edge = len(list_qubit_edge) count_gate = len(list_gate_qubits) if self.objective_name == "fidelity": list_logfidelity_single = [ int(1000 * math.log(device.list_fidelity_single[n])) for n in range(count_physical_qubit)] list_logfidelity_two = [ int(1000 * math.log(device.list_fidelity_two[k])) for k in range(count_qubit_edge)] list_logfidelity_measure = [ int(1000 * math.log(device.list_fidelity_measure[n])) for n in range(count_physical_qubit)] list_gate_two = list() list_gate_single = list() for l in range(count_gate): if len(list_gate_qubits[l]) == 1: list_gate_single.append(l) else: list_gate_two.append(l) # list_adjacency_qubit takes in a physical qubit index _p_, and # returns the list of indices of physical qubits adjacent to _p_ list_adjacent_qubit = list() # list_span_edge takes in a physical qubit index _p_, # and returns the list of edges spanned from _p_ list_span_edge = list() for n in range(count_physical_qubit): list_adjacent_qubit.append(list()) list_span_edge.append(list()) for k in range(count_qubit_edge): list_adjacent_qubit[list_qubit_edge[k][0]].append( list_qubit_edge[k][1]) list_adjacent_qubit[list_qubit_edge[k][1]].append( list_qubit_edge[k][0]) list_span_edge[list_qubit_edge[k][0]].append(k) list_span_edge[list_qubit_edge[k][1]].append(k) # if_overlap_edge takes in two edge indices _e_ and _e'_, # and returns whether or not they overlap if_overlap_edge = [[0] * count_qubit_edge for k in range(count_qubit_edge)] # list_over_lap_edge takes in an edge index _e_, # and returnsthe list of edges that overlap with _e_ list_overlap_edge = list() # list_count_overlap_edge is the list of lengths of # overlap edge lists of all the _e_ list_count_overlap_edge = list() for k in range(count_qubit_edge): list_overlap_edge.append(list()) for k in range(count_qubit_edge): for kk in range(k + 1, count_qubit_edge): if ( (list_qubit_edge[k][0] == list_qubit_edge[kk][0] or list_qubit_edge[k][0] == list_qubit_edge[kk][1]) or (list_qubit_edge[k][1] == list_qubit_edge[kk][0] or list_qubit_edge[k][1] == list_qubit_edge[kk][1]) ): list_overlap_edge[k].append(kk) list_overlap_edge[kk].append(k) if_overlap_edge[kk][k] = 1 if_overlap_edge[k][kk] = 1 for k in range(count_qubit_edge): list_count_overlap_edge.append(len(list_overlap_edge[k])) if not self.inpput_dependency: list_gate_dependency = collision_extracting(list_gate_qubits) else: list_gate_dependency = self.list_gate_dependency # index function: takes two physical qubit indices _p_ and _p'_, # and returns the index of the edge between them if there is one map_edge_index = [[0] * count_physical_qubit] * count_physical_qubit for k in range(count_qubit_edge): map_edge_index[list_qubit_edge[k][0]][list_qubit_edge[k][1]] = k map_edge_index[list_qubit_edge[k][1]][list_qubit_edge[k][0]] = k not_solved = True start_time = datetime.datetime.now() while not_solved: print("Trying maximal depth = {}...".format(bound_depth)) # variable setting # at cycle t, logical qubit q is mapped to pi[q][t] pi = [[Int("map_q{}_t{}".format(i, j)) for j in range(bound_depth)] for i in range(count_program_qubit)] # time coordinate for gate l is time[l] time = IntVector('time', count_gate) # space coordinate for gate l is space[l] space = IntVector('space', count_gate) # if at cycle t, a SWAP finishing on edge k, then sigma[k][t]=1 sigma = [[Bool("ifswap_e{}_t{}".format(i, j)) for j in range(bound_depth)] for i in range(count_qubit_edge)] # for depth optimization depth = Int('depth') # for swap optimization count_swap = Int('num_swap') # for fidelity optimization if objective_name == "fidelity": u = [Int("num_1qbg_p{}".format(n)) for n in range(count_physical_qubit)] v = [Int("num_2qbg_e{}".format(k)) for k in range(count_qubit_edge)] vv = [Int("num_swap_e{}".format(k)) for k in range(count_qubit_edge)] w = [Int("num_meas_p{}".format(n)) for n in range(count_physical_qubit)] fidelity = Int('log_fidelity') lsqc = Optimize() # constraint setting for t in range(bound_depth): for m in range(count_program_qubit): lsqc.add(pi[m][t] >= 0, pi[m][t] < count_physical_qubit) for mm in range(m): lsqc.add(pi[m][t] != pi[mm][t]) for l in range(count_gate): lsqc.add(time[l] >= 0, time[l] < bound_depth) if l in list_gate_single: lsqc.add(space[l] >= 0, space[l] < count_physical_qubit) for t in range(bound_depth): lsqc.add(Implies(time[l] == t, pi[list_gate_qubits[l][0]][t] == space[l])) elif l in list_gate_two: lsqc.add(space[l] >= 0, space[l] < count_qubit_edge) for k in range(count_qubit_edge): for t in range(bound_depth): lsqc.add(Implies(And(time[l] == t, space[l] == k), Or(And(list_qubit_edge[k][0] == \ pi[list_gate_qubits[l][0]][t], list_qubit_edge[k][1] == \ pi[list_gate_qubits[l][1]][t]), And(list_qubit_edge[k][1] == \ pi[list_gate_qubits[l][0]][t], list_qubit_edge[k][0] == \ pi[list_gate_qubits[l][1]][t]) ) )) for d in list_gate_dependency: if self.if_transition_based: lsqc.add(time[d[0]] <= time[d[1]]) else: lsqc.add(time[d[0]] < time[d[1]]) for t in range(min(swap_duration - 1, bound_depth)): for k in range(count_qubit_edge): lsqc.add(sigma[k][t] == False) for t in range(swap_duration - 1, bound_depth): for k in range(count_qubit_edge): for tt in range(t - swap_duration + 1, t): lsqc.add(Implies(sigma[k][t] == True, sigma[k][tt] == False)) for tt in range(t - swap_duration + 1, t + 1): for kk in list_overlap_edge[k]: lsqc.add(Implies(sigma[k][t] == True, sigma[kk][tt] == False)) if not self.if_transition_based: for t in range(swap_duration - 1, bound_depth): for k in range(count_qubit_edge): for tt in range(t - swap_duration + 1, t + 1): for l in range(count_gate): if l in list_gate_single: lsqc.add(Implies(And(time[l] == tt, Or(space[l] == list_qubit_edge[k][0], space[l] == list_qubit_edge[k][1])), sigma[k][t] == False )) elif l in list_gate_two: lsqc.add(Implies(And( time[l] == tt, space[l] == k), sigma[k][t] == False )) for kk in list_overlap_edge[k]: lsqc.add(Implies(And( time[l] == tt, space[l] == kk), sigma[k][t] == False )) for t in range(bound_depth - 1): for n in range(count_physical_qubit): for m in range(count_program_qubit): lsqc.add( Implies(And(sum([If(sigma[k][t], 1, 0) for k in list_span_edge[n]]) == 0, pi[m][t] == n), pi[m][t + 1] == n)) for t in range(bound_depth - 1): for k in range(count_qubit_edge): for m in range(count_program_qubit): lsqc.add(Implies(And(sigma[k][t] == True, pi[m][t] == list_qubit_edge[k][0]), pi[m][t + 1] == list_qubit_edge[k][1])) lsqc.add(Implies(And(sigma[k][t] == True, pi[m][t] == list_qubit_edge[k][1]), pi[m][t + 1] == list_qubit_edge[k][0])) lsqc.add( count_swap == sum([If(sigma[k][t], 1, 0) for k in range(count_qubit_edge) for t in range(bound_depth)])) # for depth optimization for l in range(count_gate): lsqc.add(depth >= time[l] + 1) if objective_name == "swap": lsqc.minimize(count_swap) elif objective_name == "depth": lsqc.minimize(depth) elif objective_name == "fidelity": for n in range(count_physical_qubit): lsqc.add(u[n] == sum([If(space[l] == n, 1, 0) for l in list_gate_single])) lsqc.add(w[n] == sum([If(pi[m][bound_depth - 1] == n, 1, 0) for m in range(count_program_qubit)])) for k in range(count_qubit_edge): lsqc.add(v[k] == sum([If(space[l] == k, 1, 0) for l in list_gate_two])) lsqc.add(vv[k] == sum([If(sigma[k][t], 1, 0) for t in range(bound_depth)])) lsqc.add(fidelity == sum([v[k] * list_logfidelity_two[k] for k in range(count_qubit_edge)]) + sum([ swap_duration * vv[k] * list_logfidelity_two[k] for k in range(count_qubit_edge)]) + sum([w[n] * list_logfidelity_measure[n] for n in range(count_physical_qubit)]) + sum([u[n] * list_logfidelity_single[n] for n in range(count_physical_qubit)]) ) lsqc.maximize(fidelity) else: raise Exception("Invalid Objective Name") satisfiable = lsqc.check() if satisfiable == sat: not_solved = False else: if self.if_transition_based: bound_depth += 1 else: bound_depth = int(1.3 * bound_depth) print(f"Compilation time = {datetime.datetime.now() - start_time}.") model = lsqc.model() # post-processing result_time = [] result_depth = model[depth].as_long() for l in range(count_gate): result_time.append(model[time[l]].as_long()) list_result_swap = [] for k in range(count_qubit_edge): for t in range(result_depth): if model[sigma[k][t]]: list_result_swap.append((k, t)) print(f"SWAP on physical edge ({list_qubit_edge[k][0]},"\ + f"{list_qubit_edge[k][1]}) at time {t}") for l in range(count_gate): if len(list_gate_qubits[l]) == 1: qq = list_gate_qubits[l][0] tt = result_time[l] print(f"Gate {l}: {list_gate_name[l]} {qq} on qubit "\ + f"{model[pi[qq][tt]].as_long()} at time {tt}") else: qq = list_gate_qubits[l][0] qqq = list_gate_qubits[l][1] tt = result_time[l] print(f"Gate {l}: {list_gate_name[l]} {qq}, {qqq} on qubits "\ + f"{model[pi[qq][tt]].as_long()} and "\ + f"{model[pi[qqq][tt]].as_long()} at time {tt}") # transition based if self.if_transition_based: self.swap_duration = self.device.swap_duration map_to_block = dict() real_time = [0 for i in range(count_gate)] list_depth_on_qubit = [-1 for i in range(self.count_physical_qubit)] list_real_swap = [] for block in range(result_depth): for tmp_gate in range(count_gate): if result_time[tmp_gate] == block: qubits = list_gate_qubits[tmp_gate] if len(qubits) == 1: p0 = model[pi[qubits[0]][block]].as_long() real_time[tmp_gate] = \ list_depth_on_qubit[p0] + 1 list_depth_on_qubit[p0] = \ real_time[tmp_gate] map_to_block[real_time[tmp_gate]] = block else: p0 = model[pi[qubits[0]][block]].as_long() p1 = model[pi[qubits[1]][block]].as_long() real_time[tmp_gate] = max( list_depth_on_qubit[p0], list_depth_on_qubit[p1]) + 1 list_depth_on_qubit[p0] = \ real_time[tmp_gate] list_depth_on_qubit[p1] = \ real_time[tmp_gate] map_to_block[real_time[tmp_gate]] = block # print(f"{tmp_gate} {p0} {p1} real-time={real_time[tmp_gate]}") if block < result_depth - 1: for (k, t) in list_result_swap: if t == block: p0 = list_qubit_edge[k][0] p1 = list_qubit_edge[k][1] tmp_time = max(list_depth_on_qubit[p0], list_depth_on_qubit[p1]) \ + self.swap_duration list_depth_on_qubit[p0] = tmp_time list_depth_on_qubit[p1] = tmp_time list_real_swap.append((k, tmp_time)) # print(list_depth_on_qubit) result_time = real_time real_depth = 0 for tmp_depth in list_depth_on_qubit: if real_depth < tmp_depth + 1: real_depth = tmp_depth + 1 result_depth = real_depth list_result_swap = list_real_swap # print(list_result_swap) objective_value = 0 if objective_name == "fidelity": objective_value = model[fidelity].as_long() print(f"result fidelity = {math.exp(objective_value / 1000.0)}") elif objective_name == "swap": objective_value = len(list_result_swap) print(f"result additional SWAP count = {objective_value}.") else: objective_value = model[depth].as_long() print(f"result circuit depth = {objective_value}.") list_scheduled_gate_qubits = [[] for i in range(result_depth)] list_scheduled_gate_name = [[] for i in range(result_depth)] result_depth = 0 for l in range(count_gate): t = result_time[l] if result_depth < t + 1: result_depth = t + 1 list_scheduled_gate_name[t].append(list_gate_name[l]) if l in list_gate_single: q = model[space[l]].as_long() list_scheduled_gate_qubits[t].append((q,)) elif l in list_gate_two: [q0, q1] = list_gate_qubits[l] tmp_t = t if self.if_transition_based: tmp_t = map_to_block[t] q0 = model[pi[q0][tmp_t]].as_long() q1 = model[pi[q1][tmp_t]].as_long() list_scheduled_gate_qubits[t].append((q0, q1)) else: raise ValueError("Expect single-qubit or two-qubit gate.") final_mapping = [] for m in range(count_program_qubit): tmp_depth = result_depth - 1 if self.if_transition_based: tmp_depth = map_to_block[result_depth - 1] final_mapping.append(model[pi[m][tmp_depth]].as_long()) for (k, t) in list_result_swap: q0 = list_qubit_edge[k][0] q1 = list_qubit_edge[k][1] if self.swap_duration == 1: list_scheduled_gate_qubits[t].append((q0, q1)) list_scheduled_gate_name[t].append("SWAP") elif self.swap_duration == 3: list_scheduled_gate_qubits[t].append((q0, q1)) list_scheduled_gate_name[t].append("cx") list_scheduled_gate_qubits[t - 1].append((q1, q0)) list_scheduled_gate_name[t - 1].append("cx") list_scheduled_gate_qubits[t - 2].append((q0, q1)) list_scheduled_gate_name[t - 2].append("cx") else: raise ValueError("Expect SWAP duration one, or three") if output_mode == "IR": if output_file_name: output_file = open(output_file_name, 'w') output_file.writelines([list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping, objective_value]) return (result_depth, list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping, objective_value) else: return (output_qasm(device, result_depth, list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping, True, output_file_name), final_mapping, objective_value)
class CrosstalkAdaptiveSchedule(TransformationPass): """Crosstalk mitigation through adaptive instruction scheduling.""" def __init__(self, backend_prop, crosstalk_prop, weight_factor=0.5, measured_qubits=None): """CrosstalkAdaptiveSchedule initializer. Args: backend_prop (BackendProperties): backend properties object crosstalk_prop (dict): crosstalk properties object crosstalk_prop[g1][g2] specifies the conditional error rate of g1 when g1 and g2 are executed simultaneously. g1 should be a two-qubit tuple of the form (x,y) where x and y are physical qubit ids. g2 can be either two-qubit tuple (x,y) or single-qubit tuple (x). We currently ignore crosstalk between pairs of single-qubit gates. Gate pairs which are not specified are assumed to be crosstalk free. Example:: crosstalk_prop = {(0, 1) : {(2, 3) : 0.2, (2) : 0.15}, (4, 5) : {(2, 3) : 0.1}, (2, 3) : {(0, 1) : 0.05, (4, 5): 0.05}} The keys of the crosstalk_prop are tuples for ordered tuples for CX gates e.g., (0, 1) corresponding to CX 0, 1 in the hardware. Each key has an associated value dict which specifies the conditional error rates with nearby gates e.g., ``(0, 1) : {(2, 3) : 0.2, (2) : 0.15}`` means that CNOT 0, 1 has an error rate of 0.2 when it is executed in parallel with CNOT 2,3 and an error rate of 0.15 when it is executed in parallel with a single qubit gate on qubit 2. weight_factor (float): weight of gate error/crosstalk terms in the objective :math:`weight_factor*fidelities + (1-weight_factor)*decoherence errors`. Weight can be varied from 0 to 1, with 0 meaning that only decoherence errors are optimized and 1 meaning that only crosstalk errors are optimized. weight_factor should be tuned per application to get the best results. measured_qubits (list): a list of qubits that will be measured in a particular circuit. This arg need not be specified for circuits which already include measure gates. The arg is useful when a subsequent module such as state_tomography_circuits inserts the measure gates. If CrosstalkAdaptiveSchedule is made aware of those measurements, it is included in the optimization. Raises: ImportError: if unable to import z3 solver """ super().__init__() self.backend_prop = backend_prop self.crosstalk_prop = crosstalk_prop self.weight_factor = weight_factor if measured_qubits is None: self.input_measured_qubits = [] else: self.input_measured_qubits = measured_qubits self.bp_u1_err = {} self.bp_u1_dur = {} self.bp_u2_err = {} self.bp_u2_dur = {} self.bp_u3_err = {} self.bp_u3_dur = {} self.bp_cx_err = {} self.bp_cx_dur = {} self.bp_t1_time = {} self.bp_t2_time = {} self.gate_id = {} self.gate_start_time = {} self.gate_duration = {} self.gate_fidelity = {} self.overlap_amounts = {} self.overlap_indicator = {} self.qubit_lifetime = {} self.dag_overlap_set = {} self.xtalk_overlap_set = {} self.opt = Optimize() self.measured_qubits = [] self.measure_start = None self.last_gate_on_qubit = None self.first_gate_on_qubit = None self.fidelity_terms = [] self.coherence_terms = [] self.model = None self.dag = None self.parse_backend_properties() def powerset(self, iterable): """ Finds the set of all subsets of the given iterable This function is used to generate constraints for the Z3 optimization """ l_s = list(iterable) return chain.from_iterable( combinations(l_s, r) for r in range(len(l_s) + 1)) def parse_backend_properties(self): """ This function assumes that gate durations and coherence times are in seconds in backend.properties() This function converts gate durations and coherence times to nanoseconds. """ backend_prop = self.backend_prop for qid in range(len(backend_prop.qubits)): self.bp_t1_time[qid] = int(backend_prop.t1(qid) * 10**9) self.bp_t2_time[qid] = int(backend_prop.t2(qid) * 10**9) self.bp_u1_dur[qid] = int(backend_prop.gate_length('u1', qid)) * 10**9 u1_err = backend_prop.gate_error('u1', qid) if u1_err == 1.0: u1_err = 0.9999 self.bp_u1_err = round(u1_err, NUM_PREC) self.bp_u2_dur[qid] = int(backend_prop.gate_length('u2', qid)) * 10**9 u2_err = backend_prop.gate_error('u2', qid) if u2_err == 1.0: u2_err = 0.9999 self.bp_u2_err = round(u2_err, NUM_PREC) self.bp_u3_dur[qid] = int(backend_prop.gate_length('u3', qid)) * 10**9 u3_err = backend_prop.gate_error('u3', qid) if u3_err == 1.0: u3_err = 0.9999 self.bp_u3_err = round(u3_err, NUM_PREC) for ginfo in backend_prop.gates: if ginfo.gate == 'cx': q_0 = ginfo.qubits[0] q_1 = ginfo.qubits[1] cx_tup = (min(q_0, q_1), max(q_0, q_1)) self.bp_cx_dur[cx_tup] = int( backend_prop.gate_length('cx', cx_tup)) * 10**9 cx_err = backend_prop.gate_error('cx', cx_tup) if cx_err == 1.0: cx_err = 0.9999 self.bp_cx_err[cx_tup] = round(cx_err, NUM_PREC) def cx_tuple(self, gate): """ Representation for two-qubit gate Note: current implementation assumes that the CX error rates and crosstalk behavior are independent of gate direction """ physical_q_0 = gate.qargs[0].index physical_q_1 = gate.qargs[1].index r_0 = min(physical_q_0, physical_q_1) r_1 = max(physical_q_0, physical_q_1) return (r_0, r_1) def singleq_tuple(self, gate): """ Representation for single-qubit gate """ physical_q_0 = gate.qargs[0].index tup = (physical_q_0, ) return tup def gate_tuple(self, gate): """ Representation for gate """ if len(gate.qargs) == 2: return self.cx_tuple(gate) else: return self.singleq_tuple(gate) def assign_gate_id(self, dag): """ ID for each gate """ idx = 0 for gate in dag.gate_nodes(): self.gate_id[gate] = idx idx += 1 def extract_dag_overlap_sets(self, dag): """ Gate A, B are overlapping if A is neither a descendant nor an ancestor of B. Currenty overlaps (A,B) are considered when A is a 2q gate and B is either 2q or 1q gate. """ for gate in dag.two_qubit_ops(): overlap_set = [] descendants = dag.descendants(gate) ancestors = dag.ancestors(gate) for tmp_gate in dag.gate_nodes(): if tmp_gate == gate: continue if tmp_gate in descendants: continue if tmp_gate in ancestors: continue overlap_set.append(tmp_gate) self.dag_overlap_set[gate] = overlap_set def is_significant_xtalk(self, gate1, gate2): """ Given two conditional gate error rates check if there is high crosstalk by comparing with independent error rates. """ gate1_tup = self.gate_tuple(gate1) if len(gate2.qargs) == 2: gate2_tup = self.gate_tuple(gate2) independent_err_g_1 = self.bp_cx_err[gate1_tup] independent_err_g_2 = self.bp_cx_err[gate2_tup] rg_1 = self.crosstalk_prop[gate1_tup][ gate2_tup] / independent_err_g_1 rg_2 = self.crosstalk_prop[gate2_tup][ gate1_tup] / independent_err_g_2 if rg_1 > TWOQ_XTALK_THRESH or rg_2 > TWOQ_XTALK_THRESH: return True else: gate2_tup = self.gate_tuple(gate2) independent_err_g_1 = self.bp_cx_err[gate1_tup] rg_1 = self.crosstalk_prop[gate1_tup][ gate2_tup] / independent_err_g_1 if rg_1 > ONEQ_XTALK_THRESH: return True return False def extract_crosstalk_relevant_sets(self): """ Extract the set of program gates which potentially have crosstalk noise """ for gate in self.dag_overlap_set: self.xtalk_overlap_set[gate] = [] tup_g = self.gate_tuple(gate) if tup_g not in self.crosstalk_prop: continue for par_g in self.dag_overlap_set[gate]: tup_par_g = self.gate_tuple(par_g) if tup_par_g in self.crosstalk_prop[tup_g]: if self.is_significant_xtalk(gate, par_g): if par_g not in self.xtalk_overlap_set[gate]: self.xtalk_overlap_set[gate].append(par_g) def create_z3_vars(self): """ Setup the variables required for Z3 optimization """ for gate in self.dag.gate_nodes(): t_var_name = 't_' + str(self.gate_id[gate]) d_var_name = 'd_' + str(self.gate_id[gate]) f_var_name = 'f_' + str(self.gate_id[gate]) self.gate_start_time[gate] = Real(t_var_name) self.gate_duration[gate] = Real(d_var_name) self.gate_fidelity[gate] = Real(f_var_name) for gate in self.xtalk_overlap_set: self.overlap_indicator[gate] = {} self.overlap_amounts[gate] = {} for g_1 in self.xtalk_overlap_set: for g_2 in self.xtalk_overlap_set[g_1]: if len(g_2.qargs) == 2 and g_1 in self.overlap_indicator[g_2]: self.overlap_indicator[g_1][g_2] = self.overlap_indicator[ g_2][g_1] self.overlap_amounts[g_1][g_2] = self.overlap_amounts[g_2][ g_1] else: # Indicator variable for overlap of g_1 and g_2 var_name1 = 'olp_ind_' + str( self.gate_id[g_1]) + '_' + str(self.gate_id[g_2]) self.overlap_indicator[g_1][g_2] = Bool(var_name1) var_name2 = 'olp_amnt_' + str( self.gate_id[g_1]) + '_' + str(self.gate_id[g_2]) self.overlap_amounts[g_1][g_2] = Real(var_name2) active_qubits_list = [] for gate in self.dag.gate_nodes(): for q in gate.qargs: active_qubits_list.append(q.index) for active_qubit in list(set(active_qubits_list)): q_var_name = 'l_' + str(active_qubit) self.qubit_lifetime[active_qubit] = Real(q_var_name) meas_q = [] for node in self.dag.op_nodes(): if isinstance(node.op, Measure): meas_q.append(node.qargs[0].index) self.measured_qubits = list( set(self.input_measured_qubits).union(set(meas_q))) self.measure_start = Real('meas_start') def basic_bounds(self): """ Basic variable bounds for optimization """ for gate in self.gate_start_time: self.opt.add(self.gate_start_time[gate] >= 0) for gate in self.gate_duration: q_0 = gate.qargs[0].index if isinstance(gate.op, U1Gate): dur = self.bp_u1_dur[q_0] elif isinstance(gate.op, U2Gate): dur = self.bp_u2_dur[q_0] elif isinstance(gate.op, U3Gate): dur = self.bp_u3_dur[q_0] elif isinstance(gate.op, CXGate): dur = self.bp_cx_dur[self.cx_tuple(gate)] self.opt.add(self.gate_duration[gate] == dur) def scheduling_constraints(self): """ DAG scheduling constraints optimization Sets overlap indicator variables """ for gate in self.gate_start_time: for dep_gate in self.dag.successors(gate): if not dep_gate.type == 'op': continue if isinstance(dep_gate.op, Measure): continue if isinstance(dep_gate.op, Barrier): continue fin_g = self.gate_start_time[gate] + self.gate_duration[gate] self.opt.add(self.gate_start_time[dep_gate] > fin_g) for g_1 in self.xtalk_overlap_set: for g_2 in self.xtalk_overlap_set[g_1]: if len(g_2.qargs ) == 2 and self.gate_id[g_1] > self.gate_id[g_2]: # Symmetry breaking: create only overlap variable for a pair # of gates continue s_1 = self.gate_start_time[g_1] f_1 = s_1 + self.gate_duration[g_1] s_2 = self.gate_start_time[g_2] f_2 = s_2 + self.gate_duration[g_2] # This constraint enforces full or zero overlap between two gates before = (f_1 < s_2) after = (f_2 < s_1) overlap1 = And(s_2 <= s_1, f_1 <= f_2) overlap2 = And(s_1 <= s_2, f_2 <= f_1) self.opt.add(Or(before, after, overlap1, overlap2)) intervals_overlap = And(s_2 <= f_1, s_1 <= f_2) self.opt.add( self.overlap_indicator[g_1][g_2] == intervals_overlap) def fidelity_constraints(self): """ Set gate fidelity based on gate overlap conditions """ for gate in self.gate_start_time: q_0 = gate.qargs[0].index no_xtalk = False if gate not in self.xtalk_overlap_set: no_xtalk = True elif not self.xtalk_overlap_set[gate]: no_xtalk = True if no_xtalk: if isinstance(gate.op, U1Gate): fid = math.log(1.0) elif isinstance(gate.op, U2Gate): fid = math.log(1.0 - self.bp_u2_err[q_0]) elif isinstance(gate.op, U3Gate): fid = math.log(1.0 - self.bp_u3_err[q_0]) elif isinstance(gate.op, CXGate): fid = math.log(1.0 - self.bp_cx_err[self.cx_tuple(gate)]) self.opt.add(self.gate_fidelity[gate] == round(fid, NUM_PREC)) else: comb = list(self.powerset(self.xtalk_overlap_set[gate])) xtalk_set = set(self.xtalk_overlap_set[gate]) for item in comb: on_set = item off_set = [i for i in xtalk_set if i not in on_set] clauses = [] for tmpg in on_set: clauses.append(self.overlap_indicator[gate][tmpg]) for tmpg in off_set: clauses.append(Not(self.overlap_indicator[gate][tmpg])) err = 0 if not on_set: err = self.bp_cx_err[self.cx_tuple(gate)] elif len(on_set) == 1: on_gate = on_set[0] err = self.crosstalk_prop[self.gate_tuple(gate)][ self.gate_tuple(on_gate)] else: err_list = [] for on_gate in on_set: tmp_prop = self.crosstalk_prop[self.gate_tuple( gate)] err_list.append(tmp_prop[self.gate_tuple(on_gate)]) err = max(err_list) if err == 1.0: err = 0.999999 val = round(math.log(1.0 - err), NUM_PREC) self.opt.add( Implies(And(*clauses), self.gate_fidelity[gate] == val)) def coherence_constraints(self): """ Set decoherence errors based on qubit lifetimes """ self.last_gate_on_qubit = {} for gate in self.dag.topological_op_nodes(): if isinstance(gate.op, Measure): continue if isinstance(gate.op, Barrier): continue if len(gate.qargs) == 1: q_0 = gate.qargs[0].index self.last_gate_on_qubit[q_0] = gate else: q_0 = gate.qargs[0].index q_1 = gate.qargs[1].index self.last_gate_on_qubit[q_0] = gate self.last_gate_on_qubit[q_1] = gate self.first_gate_on_qubit = {} for gate in self.dag.topological_op_nodes(): if len(gate.qargs) == 1: q_0 = gate.qargs[0].index if q_0 not in self.first_gate_on_qubit: self.first_gate_on_qubit[q_0] = gate else: q_0 = gate.qargs[0].index q_1 = gate.qargs[1].index if q_0 not in self.first_gate_on_qubit: self.first_gate_on_qubit[q_0] = gate if q_1 not in self.first_gate_on_qubit: self.first_gate_on_qubit[q_1] = gate for q in self.last_gate_on_qubit: g_last = self.last_gate_on_qubit[q] g_first = self.first_gate_on_qubit[q] finish_time = self.gate_start_time[g_last] + self.gate_duration[ g_last] start_time = self.gate_start_time[g_first] if q in self.measured_qubits: self.opt.add(self.measure_start >= finish_time) self.opt.add(self.qubit_lifetime[q] == self.measure_start - start_time) else: # All qubits get measured simultaneously whether or not they need a measurement self.opt.add(self.measure_start >= finish_time) self.opt.add(self.qubit_lifetime[q] == finish_time - start_time) def objective_function(self): """ Objective function is a weighted combination of gate errors and decoherence errors """ self.fidelity_terms = [ self.gate_fidelity[gate] for gate in self.gate_fidelity ] self.coherence_terms = [] for q in self.qubit_lifetime: val = -self.qubit_lifetime[q] / min(self.bp_t1_time[q], self.bp_t2_time[q]) self.coherence_terms.append(val) all_terms = [] for item in self.fidelity_terms: all_terms.append(self.weight_factor * item) for item in self.coherence_terms: all_terms.append((1 - self.weight_factor) * item) self.opt.maximize(Sum(all_terms)) def r2f(self, val): """ Convert Z3 Real to Python float """ return float(val.as_decimal(16).rstrip('?')) def extract_solution(self): """ Extract gate start and finish times from Z3 solution """ self.model = self.opt.model() result = {} for tmpg in self.gate_start_time: start = self.r2f(self.model[self.gate_start_time[tmpg]]) dur = self.r2f(self.model[self.gate_duration[tmpg]]) result[tmpg] = (start, start + dur) return result def solve_optimization(self): """ Setup and solve a Z3 optimization for finding the best schedule """ self.opt = Optimize() self.create_z3_vars() self.basic_bounds() self.scheduling_constraints() self.fidelity_constraints() self.coherence_constraints() self.objective_function() # Solve step self.opt.check() # Extract the schedule computed by Z3 result = self.extract_solution() return result def check_dag_dependency(self, gate1, gate2): """ gate2 is a DAG dependent of gate1 if it is a descendant of gate1 """ return gate2 in self.dag.descendants(gate1) def check_xtalk_dependency(self, t_1, t_2): """ Check if two gates have a crosstalk dependency. We do not consider crosstalk between pairs of single qubit gates. """ g_1 = t_1[0] s_1 = t_1[1] f_1 = t_1[2] g_2 = t_2[0] s_2 = t_2[1] f_2 = t_2[2] if len(g_1.qargs) == 1 and len(g_2.qargs) == 1: return False, () if s_2 <= f_1 and s_1 <= f_2: # Z3 says it's ok to overlap these gates, # so no xtalk dependency needs to be checked return False, () else: # Assert because we are iterating in Z3 gate start time order, # so if two gates are not overlapping, then the second gate has to # start after the first gate finishes assert s_2 >= f_1 # Not overlapping, but we care about this dependency if len(g_1.qargs) == 2 and len(g_2.qargs) == 2: if g_2 in self.xtalk_overlap_set[g_1]: cx1 = self.cx_tuple(g_1) cx2 = self.cx_tuple(g_2) barrier = tuple(sorted([cx1[0], cx1[1], cx2[0], cx2[1]])) return True, barrier elif len(g_1.qargs) == 1 and len(g_2.qargs) == 2: if g_1 in self.xtalk_overlap_set[g_2]: singleq = self.gate_tuple(g_1) cx1 = self.cx_tuple(g_2) print(singleq, cx1) barrier = tuple(sorted([singleq, cx1[0], cx1[1]])) return True, barrier elif len(g_1.qargs) == 2 and len(g_2.qargs) == 1: if g_2 in self.xtalk_overlap_set[g_1]: singleq = self.gate_tuple(g_2) cx1 = self.cx_tuple(g_1) barrier = tuple(sorted([singleq, cx1[0], cx1[1]])) return True, barrier # Not overlapping, and we don't care about xtalk between these two gates return False, () def filter_candidates(self, candidates, layer, layer_id, triplet): """ For a gate G and layer L, L is a candidate layer for G if no gate in L has a DAG dependency with G, and if Z3 allows gates in L and G to overlap. """ curr_gate = triplet[0] for prev_triplet in layer: prev_gate = prev_triplet[0] is_dag_dep = self.check_dag_dependency(prev_gate, curr_gate) is_xtalk_dep, _ = self.check_xtalk_dependency( prev_triplet, triplet) if is_dag_dep or is_xtalk_dep: # If there is a DAG dependency, we can't insert in any previous layer # If there is Xtalk dependency, we can (in general) insert in previous layers, # but since we are iterating in the order of gate start times, # we should only insert this gate in subsequent layers for i in range(layer_id + 1): if i in candidates: candidates.remove(i) return candidates def find_layer(self, layers, triplet): """ Find the appropriate layer for a gate """ candidates = list(range(len(layers))) for i, layer in enumerate(layers): candidates = self.filter_candidates(candidates, layer, i, triplet) if not candidates: return len(layers) # Open a new layer else: return max(candidates) # Latest acceptable layer, right-alignment def generate_barriers(self, layers): """ For each gate g, see if a barrier is required to serialize it with some previously processed gate """ barriers = [] for i, layer in enumerate(layers): barriers.append(set()) if i == 0: continue for t_2 in layer: for j in range(i): prev_layer = layers[j] for t_1 in prev_layer: is_dag_dep = self.check_dag_dependency(t_1[0], t_2[0]) is_xtalk_dep, curr_barrier = self.check_xtalk_dependency( t_1, t_2) if is_dag_dep: # Don't insert a barrier since there is a DAG dependency continue if is_xtalk_dep: # Insert a barrier for this layer barriers[-1].add(curr_barrier) return barriers def create_updated_dag(self, layers, barriers): """ Given a set of layers and barries, construct a new dag """ new_dag = DAGCircuit() for qreg in self.dag.qregs.values(): new_dag.add_qreg(qreg) for creg in self.dag.cregs.values(): new_dag.add_creg(creg) canonical_register = new_dag.qregs['q'] for i, layer in enumerate(layers): curr_barriers = barriers[i] for b in curr_barriers: current_qregs = [] for idx in b: current_qregs.append(canonical_register[idx]) new_dag.apply_operation_back(Barrier(len(b)), current_qregs, []) for triplet in layer: gate = triplet[0] new_dag.apply_operation_back(gate.op, gate.qargs, gate.cargs) for node in self.dag.op_nodes(): if isinstance(node.op, Measure): new_dag.apply_operation_back(node.op, node.qargs, node.cargs) return new_dag def enforce_schedule_on_dag(self, input_gate_times): """ Z3 outputs start times for each gate. Some gates need to be serialized to implement the Z3 schedule. This function inserts barriers to implement those serializations """ gate_times = [] for key in input_gate_times: gate_times.append( (key, input_gate_times[key][0], input_gate_times[key][1])) # Sort gates by start time sorted_gate_times = sorted(gate_times, key=operator.itemgetter(1)) layers = [] # Construct a set of layers. Each layer has a set of gates that # are allowed to fire in parallel according to Z3 for triplet in sorted_gate_times: layer_idx = self.find_layer(layers, triplet) if layer_idx == len(layers): layers.append([triplet]) else: layers[layer_idx].append(triplet) # Insert barries if necessary to enforce the above layers barriers = self.generate_barriers(layers) new_dag = self.create_updated_dag(layers, barriers) return new_dag def reset(self): """ Reset variables """ self.gate_id = {} self.gate_start_time = {} self.gate_duration = {} self.gate_fidelity = {} self.overlap_amounts = {} self.overlap_indicator = {} self.qubit_lifetime = {} self.dag_overlap_set = {} self.xtalk_overlap_set = {} self.measured_qubits = [] self.measure_start = None self.last_gate_on_qubit = None self.first_gate_on_qubit = None self.fidelity_terms = [] self.coherence_terms = [] self.model = None def run(self, dag): """ Main scheduling function """ if not HAS_Z3: raise TranspilerError( 'z3-solver is required to use CrosstalkAdaptiveSchedule. ' 'To install, run "pip install z3-solver".') self.dag = dag # process input program self.assign_gate_id(self.dag) self.extract_dag_overlap_sets(self.dag) self.extract_crosstalk_relevant_sets() # setup and solve a Z3 optimization z3_result = self.solve_optimization() # post-process to insert barriers new_dag = self.enforce_schedule_on_dag(z3_result) self.reset() return new_dag
for (r, c) in board() ]).reshape(H, W) no_scout_threatens_another_scout = [ Implies( is_scout[r][c], And([ Not(is_scout[dr][dc]) for (dr, dc) in scout_moves_from[r, c] ]) ) for (r, c) in board() if (r, c) not in lakes() ] # Clauses s = Optimize() s.add(no_scouts_in_lakes) s.add(at_most_one_scout_per_segment) s.add(no_scout_threatens_another_scout) # Objective num_scouts = Sum([ If(is_scout[r][c], 1, 0) for (r, c) in board() ]) max_scouts = s.maximize(num_scouts) if s.check() == sat: #assert s.upper(max_scouts) == 14 print("The maximum number of scouts satisfying the constraints == %s." % s.upper(max_scouts)) print(diagram(s.model())) else: print("Z3 failed to find a solution.")
if not l in intersects: intersects.append(l) # objective function O = [Int(i) * Int(j) - 1 for (i, j) in intersects] # constant indication how many variables wanted to change k = 10 opt.add(Sum(X) <= 2 * k) # print(opt.check()) # print(opt.model()) print("Begin Optimization: Maximize") # Calculate running time start_time = time.time() solution = opt.maximize(And(O)) print("--- %s seconds ---" % (time.time() - start_time)) if opt.check() == sat: while opt.check() == sat: print(solution.value()) else: print("Failed to solve.") m = opt.model() for x in street: print(m.evaluate(x))
from z3 import Optimize, Real, If x = Real('x') y = Real('y') z = Real('z') def z3abs(obj): return If(x > 0, x, -x) optimizer = Optimize() # optimizer.add(x>0.0) # optimizer.add(y>0.0) optimizer.add(x*x+y*y==1.0) optimizer.add_soft(z == x+y) optimizer.maximize(z) result = optimizer.check() print(optimizer.model())
print('Part 1:', inRange) def abs(x): return If(x >= 0, x, -x) def manhattan3d(start, finish): sX, sY, sZ = start fX, fY, fZ = finish return abs(sX - fX) + abs(sY - fY) + abs(sZ - fZ) x = Int('x') y = Int('y') z = Int('z') counts = [Int('count_' + str(i)) for i in range(len(bots))] numInRange = Int('num') distanceFromOrigin = Int('dist') o = Optimize() o.add([counts[i] == bots[i].canReachPoint(x, y, z) for i in range(len(bots))]) o.add(numInRange == sum(counts)) o.add(distanceFromOrigin == abs(x) + abs(y) + abs(z)) o.maximize(numInRange) heuristic = o.minimize(distanceFromOrigin) o.check() print("Part 2:", o.lower(heuristic))
if not l in intersects: intersects.append(l) # objective function O = [ Int(i)*Int(j) - 1 for (i, j) in intersects ] # constant indication how many variables wanted to change k = 10 opt.add( Sum(X) <= 2*k ) # print(opt.check()) # print(opt.model()) print("Begin Optimization: Maximize") # Calculate running time start_time = time.time() solution = opt.maximize( And(O) ) print("--- %s seconds ---" % (time.time() - start_time)) if opt.check() == sat: while opt.check() == sat: print(solution.value()) else: print("Failed to solve.") m = opt.model() for x in street: print( m.evaluate(x) )
with open('../input/23.txt') as f: nanobots = [list(map(int, re.findall(r'-?\d+', line))) for line in f] x, y, z, r = max(nanobots, key=lambda x: x[-1]) print(sum( abs(x - a) + abs(y - b) + abs(z - c) <= r for a, b, c, _ in nanobots)) from z3 import Int, If, Optimize def Abs(x): return If(x >= 0, x, -x) def Dist(x, y, z, a, b, c): return Abs(x - a) + Abs(y - b) + Abs(z - c) X = x, y, z = Int('x'), Int('y'), Int('z') cost = Int('cost') constraint = x * 0 for *Y, r in nanobots: constraint += If(Dist(*X, *Y) <= r, 1, 0) opt = Optimize() opt.add(cost == constraint) h1 = opt.maximize(cost) h2 = opt.minimize(Dist(*(0, 0, 0), *X)) opt.check() print(opt.lower(h2))
def solve(self, output_mode=None, output_file_name=None): objective_name = self.objective_name device = self.device list_gate_qubits = self.list_gate_qubits count_program_qubit = self.count_program_qubit list_gate_name = self.list_gate_name count_physical_qubit = self.count_physical_qubit list_qubit_edge = self.list_qubit_edge swap_duration = self.swap_duration bound_depth = self.bound_depth """ pre-processing """ count_qubit_edge = len(list_qubit_edge) count_gate = len(list_gate_qubits) if self.objective_name == "fidelity": list_logfidelity_single = [ int(1000 * math.log(device.list_fidelity_single[n])) for n in range(count_physical_qubit) ] list_logfidelity_two = [ int(1000 * math.log(device.list_fidelity_two[k])) for k in range(count_qubit_edge) ] list_logfidelity_measure = [ int(1000 * math.log(device.list_fidelity_measure[n])) for n in range(count_physical_qubit) ] list_gate_two = list() list_gate_single = list() for l in range(count_gate): if isinstance(list_gate_qubits[l], int): list_gate_single.append(l) else: list_gate_two.append(l) # list_adjacency_qubit takes in a physical qubit index _p_, and returns the list of indices of # physical qubits adjacent to _p_ list_adjacent_qubit = list() # list_span_edge takes in a physical qubit index _p_, and returns the list of edges spanned from _p_ list_span_edge = list() for n in range(count_physical_qubit): list_adjacent_qubit.append(list()) list_span_edge.append(list()) for k in range(count_qubit_edge): list_adjacent_qubit[list_qubit_edge[k][0]].append( list_qubit_edge[k][1]) list_adjacent_qubit[list_qubit_edge[k][1]].append( list_qubit_edge[k][0]) list_span_edge[list_qubit_edge[k][0]].append(k) list_span_edge[list_qubit_edge[k][1]].append(k) # if_overlap_edge takes in two edge indices _e_ and _e'_, and returns whether or not they overlap if_overlap_edge = [[0] * count_qubit_edge for k in range(count_qubit_edge)] # list_over_lap_edge takes in an edge index _e_, and returns the list of edges that overlap with _e_ list_overlap_edge = list() # list_count_overlap_edge is the list of lengths of overlap edge lists of all the _e_ list_count_overlap_edge = list() for k in range(count_qubit_edge): list_overlap_edge.append(list()) for k in range(count_qubit_edge): for kk in range(k + 1, count_qubit_edge): if (list_qubit_edge[k][0] == list_qubit_edge[kk][0] or list_qubit_edge[k][0] == list_qubit_edge[kk][1]) \ or (list_qubit_edge[k][1] == list_qubit_edge[kk][0] or list_qubit_edge[k][1] == list_qubit_edge[kk][1]): list_overlap_edge[k].append(kk) list_overlap_edge[kk].append(k) if_overlap_edge[kk][k] = 1 if_overlap_edge[k][kk] = 1 for k in range(count_qubit_edge): list_count_overlap_edge.append(len(list_overlap_edge[k])) # list_gate_dependency is the list of dependency, pairs of gate indices (_l_, _l'_), where _l_<_l'_ # _l_ and _ll_ act on a same program qubit list_gate_dependency = collision_extracting(list_gate_qubits) # index function: it takes two physical qubit indices _p_ and _p'_, # and returns the index of the edge between _p_ and _p'_, if there is one map_edge_index = [[0] * count_physical_qubit] * count_physical_qubit for k in range(count_qubit_edge): map_edge_index[list_qubit_edge[k][0]][list_qubit_edge[k][1]] = k map_edge_index[list_qubit_edge[k][1]][list_qubit_edge[k][0]] = k not_solved = True while not_solved: print("Trying maximal depth = {}...".format(bound_depth)) """ variables """ # at cycle t, logical qubit q is mapped to pi[q][t] pi = [[ Int("map_q{}_t{}".format(i, j)) for j in range(bound_depth) ] for i in range(count_program_qubit)] # time coordinate for gate l is time[l] time = IntVector('time', count_gate) # space coordinate for gate l is space[l] space = IntVector('space', count_gate) # if at cycle t, there is a SWAP finishing on edge k, then sigma[k][t]=1 sigma = [[ Bool("ifswap_e{}_t{}".format(i, j)) for j in range(bound_depth) ] for i in range(count_qubit_edge)] # for depth optimization depth = Int('depth') # for swap optimization count_swap = Int('num_swap') # for fidelity optimization if objective_name == "fidelity": u = [ Int("num_1qbg_p{}".format(n)) for n in range(count_physical_qubit) ] v = [ Int("num_2qbg_e{}".format(k)) for k in range(count_qubit_edge) ] vv = [ Int("num_swap_e{}".format(k)) for k in range(count_qubit_edge) ] w = [ Int("num_meas_p{}".format(n)) for n in range(count_physical_qubit) ] fidelity = Int('log_fidelity') lsqc = Optimize() """ Constraints """ for t in range(bound_depth): for m in range(count_program_qubit): lsqc.add(pi[m][t] >= 0, pi[m][t] < count_physical_qubit) for mm in range(m): lsqc.add(pi[m][t] != pi[mm][t]) for l in range(count_gate): lsqc.add(time[l] >= 0, time[l] < bound_depth) if l in list_gate_single: lsqc.add(space[l] >= 0, space[l] < count_physical_qubit) for t in range(bound_depth): lsqc.add( Implies(time[l] == t, pi[list_gate_qubits[l]][t] == space[l])) elif l in list_gate_two: lsqc.add(space[l] >= 0, space[l] < count_qubit_edge) for k in range(count_qubit_edge): for t in range(bound_depth): lsqc.add( Implies( And(time[l] == t, space[l] == k), Or( And( list_qubit_edge[k][0] == pi[ list_gate_qubits[l][0]][t], list_qubit_edge[k][1] == pi[ list_gate_qubits[l][1]][t]), And( list_qubit_edge[k][1] == pi[ list_gate_qubits[l][0]][t], list_qubit_edge[k][0] == pi[ list_gate_qubits[l][1]][t])))) for d in list_gate_dependency: if self.if_transition_based == True: lsqc.add(time[d[0]] <= time[d[1]]) else: lsqc.add(time[d[0]] < time[d[1]]) for t in range(min(swap_duration - 1, bound_depth)): for k in range(count_qubit_edge): lsqc.add(sigma[k][t] == False) for t in range(swap_duration - 1, bound_depth): for k in range(count_qubit_edge): for tt in range(t - swap_duration + 1, t): lsqc.add( Implies(sigma[k][t] == True, sigma[k][tt] == False)) for tt in range(t - swap_duration + 1, t + 1): for kk in list_overlap_edge[k]: lsqc.add( Implies(sigma[k][t] == True, sigma[kk][tt] == False)) if self.if_transition_based == False: for t in range(swap_duration - 1, bound_depth): for k in range(count_qubit_edge): for tt in range(t - swap_duration + 1, t + 1): for l in range(count_gate): if l in list_gate_single: lsqc.add( Implies( And( time[l] == tt, Or( space[l] == list_qubit_edge[k][0], space[l] == list_qubit_edge[k][1])), sigma[k][t] == False)) elif l in list_gate_two: lsqc.add( Implies( And(time[l] == tt, space[l] == k), sigma[k][t] == False)) for kk in list_overlap_edge[k]: lsqc.add( Implies( And(time[l] == tt, space[l] == kk), sigma[k][t] == False)) for t in range(bound_depth - 1): for n in range(count_physical_qubit): for m in range(count_program_qubit): lsqc.add( Implies( And( sum([ If(sigma[k][t], 1, 0) for k in list_span_edge[n] ]) == 0, pi[m][t] == n), pi[m][t + 1] == n)) for t in range(bound_depth - 1): for k in range(count_qubit_edge): for m in range(count_program_qubit): lsqc.add( Implies( And(sigma[k][t] == True, pi[m][t] == list_qubit_edge[k][0]), pi[m][t + 1] == list_qubit_edge[k][1])) lsqc.add( Implies( And(sigma[k][t] == True, pi[m][t] == list_qubit_edge[k][1]), pi[m][t + 1] == list_qubit_edge[k][0])) lsqc.add(count_swap == sum([ If(sigma[k][t], 1, 0) for k in range(count_qubit_edge) for t in range(bound_depth) ])) # for depth optimization for l in range(count_gate): lsqc.add(depth >= time[l] + 1) if objective_name == "swap": lsqc.minimize(count_swap) elif objective_name == "depth": lsqc.minimize(depth) elif objective_name == "fidelity": for n in range(count_physical_qubit): lsqc.add(u[n] == sum( [If(space[l] == n, 1, 0) for l in list_gate_single])) lsqc.add(w[n] == sum([ If(pi[m][bound_depth - 1] == n, 1, 0) for m in range(count_program_qubit) ])) for k in range(count_qubit_edge): lsqc.add(v[k] == sum( [If(space[l] == k, 1, 0) for l in list_gate_two])) lsqc.add(vv[k] == sum( [If(sigma[k][t], 1, 0) for t in range(bound_depth)])) lsqc.add(fidelity == sum([ v[k] * list_logfidelity_two[k] for k in range(count_qubit_edge) ]) + sum([ swap_duration * vv[k] * list_logfidelity_two[k] for k in range(count_qubit_edge) ]) + sum([ w[n] * list_logfidelity_measure[n] for n in range(count_physical_qubit) ]) + sum([ u[n] * list_logfidelity_single[n] for n in range(count_physical_qubit) ])) lsqc.maximize(fidelity) else: raise Exception("Invalid Objective Name") satisfiable = lsqc.check() if satisfiable == sat: not_solved = False else: if self.if_transition_based == True: bound_depth += 1 else: bound_depth = int(1.3 * bound_depth) # print("Compilation time = {}s.".format(sys_time.time() - start_time)) model = lsqc.model() """ post-processing """ result_time = [] result_depth = model[depth].as_long() for l in range(count_gate): result_time.append(model[time[l]].as_long()) list_result_swap = [] for k in range(count_qubit_edge): for t in range(result_depth): if model[sigma[k][t]]: list_result_swap.append((k, t)) # transition based if self.if_transition_based: self.swap_duration = self.device.swap_duration map_to_block = dict() real_time = [0 for i in range(count_gate)] list_depth_on_qubit = [-1 for i in range(self.count_program_qubit)] list_real_swap = [] for block in range(result_depth): for tmp_gate in range(count_gate): if result_time[tmp_gate] == block: qubits = list_gate_qubits[tmp_gate] if isinstance(qubits, int): real_time[ tmp_gate] = list_depth_on_qubit[qubits] + 1 list_depth_on_qubit[qubits] = real_time[tmp_gate] map_to_block[real_time[tmp_gate]] = block else: real_time[tmp_gate] = list_depth_on_qubit[ qubits[0]] + 1 if real_time[tmp_gate] < list_depth_on_qubit[ qubits[1]] + 1: real_time[tmp_gate] = list_depth_on_qubit[ qubits[1]] + 1 list_depth_on_qubit[ qubits[0]] = real_time[tmp_gate] list_depth_on_qubit[ qubits[1]] = real_time[tmp_gate] map_to_block[real_time[tmp_gate]] = block if block < result_depth - 1: for (k, t) in list_result_swap: if t == block: q0 = list_qubit_edge[k][0] q1 = list_qubit_edge[k][1] tmp_time = list_depth_on_qubit[ q0] + self.swap_duration if tmp_time < list_depth_on_qubit[ q1] + self.swap_duration: tmp_time = list_depth_on_qubit[ q1] + self.swap_duration list_depth_on_qubit[q0] = tmp_time list_depth_on_qubit[q1] = tmp_time list_real_swap.append((k, tmp_time)) result_time = real_time real_depth = 0 for tmp_depth in list_depth_on_qubit: if real_depth < tmp_depth + 1: real_depth = tmp_depth + 1 result_depth = real_depth list_result_swap = list_real_swap if objective_name == "fidelity": log_fidelity = model[fidelity].as_long() print("result fidelity = {}".format(math.exp(log_fidelity / 1000.0))) elif objective_name == "swap": print("result additional SWAP count = {}.".format( len(list_result_swap))) else: print("result circuit depth = {}.".format(result_depth)) list_scheduled_gate_qubits = [[] for i in range(result_depth)] list_scheduled_gate_name = [[] for i in range(result_depth)] result_depth = 0 for l in range(count_gate): t = result_time[l] if result_depth < t + 1: result_depth = t + 1 list_scheduled_gate_name[t].append(list_gate_name[l]) if l in list_gate_single: q = model[space[l]].as_long() list_scheduled_gate_qubits[t].append(q) elif l in list_gate_two: [q0, q1] = list_gate_qubits[l] tmp_t = t if self.if_transition_based: tmp_t = map_to_block[t] q0 = model[pi[q0][tmp_t]].as_long() q1 = model[pi[q1][tmp_t]].as_long() list_scheduled_gate_qubits[t].append([q0, q1]) else: raise Exception("Expect single- or two-qubit gate.") final_mapping = [] for m in range(count_program_qubit): tmp_depth = result_depth - 1 if self.if_transition_based: tmp_depth = map_to_block[result_depth - 1] final_mapping.append(model[pi[m][tmp_depth]].as_long()) # print("logical qubit {} is mapped to node {} in the beginning, node {} at the end".format( # m, model[pi[m][0]], model[pi[m][result_depth - 1]])) for (k, t) in list_result_swap: q0 = list_qubit_edge[k][0] q1 = list_qubit_edge[k][1] if self.swap_duration == 1: list_scheduled_gate_qubits[t].append([q0, q1]) list_scheduled_gate_name[t].append("SWAP") elif self.swap_duration == 3: list_scheduled_gate_qubits[t].append([q0, q1]) list_scheduled_gate_name[t].append("cx") list_scheduled_gate_qubits[t - 1].append([q1, q0]) list_scheduled_gate_name[t - 1].append("cx") list_scheduled_gate_qubits[t - 2].append([q0, q1]) list_scheduled_gate_name[t - 2].append("cx") else: raise Exception( "Expect SWAP duration one, or three (decomposed into CX gates)" ) if output_mode == "IR": if output_file_name: output_file = open(output_file_name, 'w') output_file.writelines([ result_depth, list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping ]) return [ result_depth, list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping ] else: return output_qasm(self.device, result_depth, list_scheduled_gate_name, list_scheduled_gate_qubits, final_mapping, True, output_file_name)
return If(x >= 0, x, -x) def z3_dist(v1, v2): return z3_abs(v1.x - v2.x) + z3_abs(v1.y - v2.y) + z3_abs(v1.z - v2.z) x = Int('x') y = Int('y') z = Int('z') orig = (x, y, z) cost = Int('cost') cost_expr = 0 for bot in bots: cost_expr += If(z3_dist(Vector3(*orig), bot.pos) <= bot.r, 1, 0) opt = Optimize() print("let's go") opt.add(cost == cost_expr) opt.maximize(cost) opt.minimize(z3_dist(Vector3(0, 0, 0), Vector3(x, y, z))) opt.check() model = opt.model() print(model) pos = (model[x].as_long(), model[y].as_long(), model[z].as_long()) print("position:", pos) print("num in range:", model[cost].as_long()) print("distance:", l1(Vector3(0, 0, 0), Vector3(*pos)))