def find_min(constraints, expr, default_min=0): if type(expr) == int: return expr constraint_strs = [f'{c}' for c in constraints] min_optimize = Optimize() min_optimize.set('timeout', 10000) min_optimize.assert_exprs(*constraints) min_optimize.minimize(expr) status = min_optimize.check() if status != sat: print(f'Unable to find min ({status}) for:\n' + '\n'.join(constraint_strs)) return None min_val = min_optimize.model().eval(expr).as_long() # Make sure it's actually the min, since z3 has a bug # https://github.com/Z3Prover/z3/issues/4670 solver = Solver() solver.set('timeout', 10000) solver.add(constraints + [expr < min_val]) status = solver.check() if status != unsat: print( f'Z3 bug\nFind min ({expr}) => {min_val} with status ({status}):\n' + '\n'.join(constraint_strs)) return None return min_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 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))
def to_z3_problem(problem: DependencyProblem, states: List[EncodedState]) -> Optimize: minimizer = Optimize() minimizer.add(to_formula(problem, states)) cost = Int('cost') cost_constraint = cost == total_cost(states, problem.repository) minimizer.add(cost_constraint) minimizer.minimize(cost) return minimizer
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 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)
def solve_boolean_formula_with_z3_smt2(self, bf): """Find minimum satisfying assignemnt for the boolean formula. # Example: # >>> bf = '(and (or a b) (not (and a c)))' # >>> appeared_symbol_list = ['a', 'b', 'c'] # >>> solve_boolean_formula_with_z3_smt2(bf, appeared_symbol_list) # ([b = True, a = False, c = False, s = 1], 1) """ appeared_symbol_list = list( set([ a if "not " not in a else a[5:-1] for a in self.prov_notations.values() ])) declaration_str = '\n'.join( list( map(lambda x: '(declare-const {} Bool)'.format(x), appeared_symbol_list))) declaration_str += '\n(declare-const s Int)' declaration_str += '\n(define-fun b2i ((x Bool)) Int (ite x 1 0))' size_str = '(+ {})'.format(' '.join( list(map(lambda x: '(b2i {})'.format(x), appeared_symbol_list)))) assert_str = '(assert {})\n'.format(bf) assert_str += '(assert (= s {}))\n(assert (>= s 0))'.format( size_str) # changed from (> s 0) z3_bf = parse_smt2_string(declaration_str + '\n' + assert_str) opt = Optimize() opt.add(z3_bf) s = Int('s') opt.minimize(s) # changed from opt.minimize(s) if opt.check() == sat: best_model = opt.model() min_size = 0 for cl in best_model: if isinstance(best_model[cl], BoolRef) and best_model[cl]: min_size += 1 return best_model, min_size else: return None, -1
def geometry_free_solve_(ddn1, ddn2, ws, station_data, sta1, sta2, prn1, prn2, ticks): lambda_1 = lambda_1s[prn1[0]] lambda_2 = lambda_2s[prn1[0]] # Φ_i - R_i = B_i + err with B_i = b_i + λ_1*N_1 - λ_2*N_2 B_i = bias(tec.geometry_free) sol = Optimize() # sol = Solver() errs = Reals('err_11 err_12 err_21 err_22') n1s = Ints('n1_11 n1_12 n1_21 n1_22') n2s = Ints('n2_11 n2_12 n2_21 n2_22') sol.add(n1s[0] - n1s[1] - n1s[2] + n1s[3] == ddn1) sol.add(n2s[0] - n2s[1] - n2s[2] + n2s[3] == ddn2) for i, (sta, prn) in enumerate(product([sta1, sta2], [prn1, prn2])): sol.add(n1s[i] - n2s[i] == ws[i]) B_i_samples = [] for tick in ticks: B_i_samples.append( B_i(station_data[sta][prn][tick])[0] ) B_i_avg = numpy.mean(B_i_samples) # B_i_avg = B_i_samples[0] print(B_i_avg, numpy.std(B_i_samples)) sol.add(lambda_1 * ToReal(n1s[i]) - lambda_2 * ToReal(n2s[i]) + errs[i] > B_i_avg) sol.add(lambda_1 * ToReal(n1s[i]) - lambda_2 * ToReal(n2s[i]) - errs[i] < B_i_avg) """ sol.add(errs[0] < .9) sol.add(errs[1] < .9) sol.add(errs[2] < .9) sol.add(errs[3] < .9) """ #sol.add(errs[0] + errs[1] + errs[2] + errs[3] < 17) objective = sol.minimize(errs[0] + errs[1] + errs[2] + errs[3]) if sol.check() != sat: return None sol.lower(objective) if sol.check() != sat: return None # sol.add(errs[0] + errs[1] + errs[2] + errs[3] < 2) # can't do L2 norm with z3, L1 will have to do... # sol.(errs[0] + errs[1] + errs[2] + errs[3]) return ( [sol.model()[n1s[i]].as_long() for i in range(4)], [sol.model()[n2s[i]].as_long() for i in range(4)], [frac_to_float(sol.model()[errs[i]]) for i in range(4)], )
def _solve( solver: z3.Optimize, channels: List[Channel], devices: List[Device] ) -> Tuple[Problem, z3.Model]: problem = _problem(solver, freqs=[c.frequency for c in channels], devices=devices) if solver.check() != z3.sat: # TODO: consider getting an unsat core raise ValueError(f"No valid assignment possible, add more devices") # find the minimal number of devices that cover all frequencies # it is faster to do this iteratively than to offload these to # smt constraints. do this in reverse so we end up assigning # the lowest numbered devices for r in problem.ranges[::-1]: # to control the device placements adjust the order they're eliminated from # the solution. for example, this will prefer devices that have a smaller # minimum sample rate over devices with a larger one: # for r, _ in zip(problem.ranges, problem.devices), key=lambda x: -x[1].min_sample_rate solver.push() solver.add(r == 0) if solver.check() != z3.sat: solver.pop() break devices_required = z3.Sum([z3.If(r > 0, 1, 0) for r in problem.ranges]) # minimize the sum of frequency ranges solver.minimize(z3.Sum(*problem.ranges)) # and the frequency, just to produce deterministic results solver.minimize(z3.Sum(*problem.lower_freq)) assert solver.check() == z3.sat model = solver.model() print(f"Devices required: {model.eval(devices_required)}", file=sys.stderr) return problem, model
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 dd_solve_(dd, vr1s1, vr1s2, vr2s1, vr2s2, wavelength, ionosphere=False): sol = Optimize() r1s1, r1s2, r2s1, r2s2 = Ints('r1s1 r1s2 r2s1 r2s2') # err = Real('err') err1, err2, err3, err4 = Reals('err1 err2 err3 err4') # sol.add(err > 0) if ionosphere: ion = Real('ion') sol.add(ion > 0) sol.add(ion < 25) else: ion = 0 sol.add(r1s1 - r1s2 - r2s1 + r2s2 == dd) sol.add(ToReal(r1s1)*wavelength + err1 > vr1s1 - ion) sol.add(ToReal(r1s1)*wavelength - err1 < vr1s1 - ion) sol.add(ToReal(r1s2)*wavelength + err2 > vr1s2 - ion) sol.add(ToReal(r1s2)*wavelength - err2 < vr1s2 - ion) sol.add(ToReal(r2s1)*wavelength + err3 > vr2s1 - ion) sol.add(ToReal(r2s1)*wavelength - err3 < vr2s1 - ion) sol.add(ToReal(r2s2)*wavelength + err4 > vr2s2 - ion) sol.add(ToReal(r2s2)*wavelength - err4 < vr2s2 - ion) objective = sol.minimize(err1 + err2 + err3 + err4) if sol.check() != sat: return None sol.lower(objective) if sol.check() != sat: return None return ( [sol.model()[r].as_long() for r in [r1s1, r1s2, r2s1, r2s2]], [frac_to_float(sol.model()[err]) for err in [err1, err2, err3, err4]], frac_to_float(sol.model()[ion]) if ionosphere else 0 )
globals()[abc[i]] = Int(abc[i]) l.append(abc[i]) s.add(And(d[abc[i]] >= 0, d[abc[i]] <= n - 1)) s.add(Distinct([d[x] for x in l])) for i in superstring.split(' '): s.add( Int('diff_{0}_{1}'.format(i[0], i[1])) == diff(d.get(i[0]), d.get( i[1]))) try: final_sum += Int('diff_{0}_{1}'.format(i[0], i[1])) * int(i[2]) except NameError: final_sum = Int('diff_{0}_{1}'.format(i[0], i[1])) * int(i[2]) final_sum2 = Int("final_sum2") s.add(final_sum == final_sum2) s.minimize(final_sum) s.check() m = s.model() a = {} for i in l: a[m[d[i]].as_long()] = str(i) for i in range(n): print(a[i]) print('Minimum number of cables: {0}'.format(m[final_sum2]))
class Oracle(ABC): """ An oracle is a dict from state_ids to values (not neccessarily probabilities since o.w. the eq system does not always have a solution). This is an abstract base class. Concrete sub-classes must overwrite `initialize`. Attributes: state_graph (StateGraph): the associated state graph default_value (Fraction): The default value is the oracle value returned if the given state_id is not a key of the oracle statistics (Statistics): access to the global statistics settings (Settings): all settings solver (Solver): a solver for the equation system oracle_states (Set[StateId]): states in this oracle oracle (Dict[StateId, z3.ExprRef]): the oracle's internal value dict """ def __init__(self, state_graph: StateGraph, default_value: Fraction, statistics: Statistics, settings: Settings, model_type: PrismModelType): self.state_graph = state_graph self.statistics = statistics self.settings = settings self.model_type = model_type if default_value < 0: raise ValueError("Oracle values must be greater or equal to 0") self.default_value = RealVal(default_value) self.solver = Solver() self.solver_mdp = Optimize() # The way we refine the Oracle depends on the model type if model_type == PrismModelType.DTMC: self.refine_oracle = self.refine_oracle_mc elif model_type == PrismModelType.MDP: self.refine_oracle = self.refine_oracle_mdp else: raise Exception("Oracle: Unsupported model type") self.oracle_states: Set[StateId] = set() self.oracle: Dict[StateId, z3.ExprRef] = dict() # self.save_oracle_on_disk() def _ensure_value_in_oracle(self, state_id: StateId): """ Used to override standard behaviour. Takes a state id, ensures that self.oracle contains this value. :param state_id: :return: """ pass def get_oracle_value(self, state_id: StateId) -> z3.ExprRef: if state_id not in self.oracle: self._ensure_value_in_oracle(state_id) return self.oracle.get(state_id, self.default_value) def refine_oracle_mc(self, visited_states: Set[StateId]) -> Set[StateId]: self.statistics.inc_refine_oracle_counter() # First ensure progress if visited_states <= self.oracle_states: # Ensure progress by adding all non-target successors of states in oracle_states to the set self.oracle_states = self.oracle_states.union({ succ_id for state_id in self.oracle_states for succ_id, prob in self.state_graph.get_filtered_successors(state_id) if succ_id != -1 }) else: self.oracle_states = self.oracle_states.union(visited_states) # TODO: A lot of optimization potential self.solver.push() # We need a variable for every oracle state variables = { state_id: Real("x_%s" % state_id) for state_id in self.oracle_states } # Set up EQ - System for state_id in self.oracle_states: self.solver.add(variables[state_id] == Sum([ RealVal(1) * prob if succ_id == -1 else # Case succ_id target state ( variables[succ_id] * prob if succ_id in self.oracle_states else # Case succ_id oracle state self.get_oracle_value(succ_id) * prob) # Case sycc_id no target and no oracle state for succ_id, prob in self.state_graph.get_filtered_successors( state_id) ])) self.solver.add(variables[state_id] >= RealVal(0)) #print(self.solver.assertions()) if self.solver.check() == sat: m = self.solver.model() # update oracle for state_id in self.oracle_states: self.oracle[state_id] = m[variables[state_id]] logger.info("Refined oracle.") #logger.info(self.oracle) self.solver.pop() return self.oracle_states else: # The oracle solver is unsat. In this case, we solve the LP. self.solver.pop() self.statistics.refine_oracle_counter = self.statistics.refine_oracle_counter - 1 return self.refine_oracle_mdp(visited_states) def refine_oracle_mdp(self, visited_states: Set[StateId]) -> Set[StateId]: self.statistics.inc_refine_oracle_counter() # First ensure progress if visited_states <= self.oracle_states: # Ensure progress by adding all non-target successors of states in oracle_states to the set (for every action) self.oracle_states = self.oracle_states.union({ succ[0] for state_id in self.oracle_states for choice in self.state_graph.get_successors_filtered(state_id).choices for succ in choice.distribution if succ[0] != -1 }) else: self.oracle_states = self.oracle_states.union(visited_states) # TODO: A lot of optimization potential self.solver_mdp.push() # We need a variable for every oracle state variables = { state_id: Real("x_%s" % state_id) for state_id in self.oracle_states } # Set up EQ - System for state_id in self.oracle_states: for choice in self.state_graph.get_successors_filtered( state_id).choices: self.solver_mdp.add(variables[state_id] >= Sum([ RealVal(1) * prob if succ_id == -1 else # Case succ_id target state ( variables[succ_id] * prob if succ_id in self.oracle_states else # Case succ_id oracle state self.get_oracle_value(succ_id) * prob) # Case sycc_id no target and no oracle state for succ_id, prob in choice.distribution ])) self.solver_mdp.add(variables[state_id] >= RealVal(0)) # Minimize value for initial state self.solver_mdp.minimize( variables[self.state_graph.get_initial_state_id()]) if self.solver_mdp.check() == sat: m = self.solver_mdp.model() # update oracle for state_id in self.oracle_states: self.oracle[state_id] = m[variables[state_id]] logger.info("Refined oracle.") # logger.info(self.oracle) self.solver_mdp.pop() return self.oracle_states else: logger.error("Oracle solver unsat") raise RuntimeError("Oracle solver inconsistent.") @abstractmethod def initialize(self): """ Stub to be overwritten by concrete oracles. :return: """ pass def save_oracle_on_disk(self): """ Save this oracle to disk using `save_oracle_dict` from `pric3.oracles.file_oracle`. """ from pric3.oracles.file_oracle import save_oracle_dict save_oracle_dict(self.state_graph, self.oracle) def _get_prism_program(self): return self.state_graph.input_program.prism_program
scout_moves_from = np.array([ list( chain(zip(repeat(r), L_scout_moves_from(r, c)), zip(repeat(r), R_scout_moves_from(r, c)), zip(D_scout_moves_from(r, c), repeat(c)), zip(U_scout_moves_from(r, c), repeat(c)))) for (r, c) in board() ]).reshape(H, W) each_square_occupied_or_threatened_by_scout = [ Or(is_scout[r][c], Or([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(each_square_occupied_or_threatened_by_scout) # Objective num_scouts = Sum([If(is_scout[r][c], 1, 0) for (r, c) in board()]) min_scouts = s.minimize(num_scouts) if s.check() == sat: assert s.lower(min_scouts) == 8 print("The minimum number of scouts satisfying the constraints == %s." % s.lower(min_scouts)) print(diagram(s.model())) else: print("Z3 failed to find a solution.")
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 StateProbabilityGenerator: def __init__(self, state_graph, statistics, settings, model_type): self.statistics = statistics self.state_graph = state_graph self.model_type = model_type logger.debug("Initialize oracle...") self._initialize_oracle(settings) logger.debug("Initialize obligation cache...") self._obligation_cache = ObligationCache() logger.debug("Initialize optimization solver...") # Initialize solver for optimization queries self.opt_solver = Optimize() self._realval_zero = RealVal(0) self._realval_one = RealVal(1) self.obligation_queue_class = settings.get_obligation_queue_class() def _initialize_oracle(self, settings): self.statistics.start_initialize_oracle_timer() args = [self.state_graph,settings.default_oracle_value,self.statistics,settings, self.model_type] if settings.oracle_type == "simulation": self.oracle = SimulationOracle(*args) elif settings.oracle_type == "perfect": self.oracle = ExactOracle(*args) elif settings.oracle_type == "modelchecking": self.oracle = ModelCheckingOracle(*args) elif settings.oracle_type == "solveeqspartly_exact" or settings.oracle_type == "solveeqspartly_inexact": self.oracle = SolveEQSPartlyOracle(*args) elif settings.oracle_type == "file": self.oracle = FileOracle(*args) else: raise RuntimeError("Unclear which oracle to use.") self.oracle.initialize() self.statistics.stop_initialize_oracle_timer() def refine_oracle(self, visited_states): res = self.oracle.refine_oracle(visited_states) self.reset_cache() return res def reset_cache(self): logger.debug("Reset obligation cache ...") self._obligation_cache.reset_cache() def finalize_statistics(self): self.statistics.set_number_oracle_states(len(self.oracle.oracle_states)) def run(self, state_id, chosen_command, delta, states_with_fixed_probabilities = set()): """ :param state_id: :param delta: :return: (1) True iff it is possible to find probabilities for the successors of the given state_id and delta. (2) If True, then it returns a dict form succ_ids to probabilities. This dict does not contain goal states. """ # TODO consider changing to None if not possible, and dict otherwise. self.statistics.inc_get_probability_counter() self.statistics.start_get_probability_timer() # First check whether we have cached the corresponding obligation res = self._obligation_cache.get_cached(state_id, chosen_command, delta) if res != False: self.statistics.stop_get_probability_timer() return (True, res) # If not, we have to ask the SMT-Solver succ_dist = self.state_graph.get_successors_filtered(state_id).by_command_index(chosen_command) succ_dist_without_target_states = [(state_id, prob) for (state_id, prob) in succ_dist if state_id != -1] # Check if there is at least one non-target state. Otherwise, repairing is not possible (smt solver would return unsat if we continued, so checking this is an optimization). if len(succ_dist_without_target_states) == 0: self.statistics.stop_get_probability_timer() return (False, None) self.opt_solver.push() vars = {} # We need a variable for each successor for (succ_id, prob) in succ_dist: if succ_id != -1: vars[succ_id] = Real("x_%s" % succ_id) # all results must of be probabilities self.opt_solver.add(vars[succ_id] >= self._realval_zero) self.opt_solver.add(vars[succ_id] <= self._realval_one) # \Phi(F)[s] = delta constraint # TODO: Type of porb is pycarl.gmp.gmp.Rational. Z3 magically deals with this self.opt_solver.add( Sum([ (vars[succ_id] if succ_id != -1 else RealVal(1)) * prob # Note: Keep in mind that you need to check whether succ is a target state for (succ_id, prob) in succ_dist ]) == delta) for (succ_id, prob) in succ_dist: if succ_id in states_with_fixed_probabilities: self.opt_solver.add(vars[succ_id] == self.obligation_queue_class.smallest_probability_for_state[succ_id]) # If we have more than one non-target successor, we have to optimize if len(succ_dist_without_target_states) > 1: # first check whether all oracle values are 0 (note that we do not have to do this if there is only one succ without target) if len(succ_dist_without_target_states) > 1 and sum([self.oracle.get_oracle_value(state_id).as_fraction() for state_id, prob in succ_dist_without_target_states]) == 0: # In this case, we require that the probability mass is distributed equally for i in range(0, len(succ_dist_without_target_states) - 1): self.opt_solver.add( vars[succ_dist_without_target_states[i][0]] == vars[succ_dist_without_target_states[i + 1][0]]) else: # First Try to solve the eq system # TODO: Do not use opt_solver for this if self._get_probabilities_by_solving_eq_system(succ_dist_without_target_states, vars): self.statistics.inc_solved_eq_system_instead_of_optimization_counter() m = self.opt_solver.model() result = { succ_id: m[vars[succ_id]] for (succ_id, prob) in succ_dist_without_target_states } # Because get_probabilities_by_solving_eq_system pushes # TODO: This is ugly # TODO: Compare solve-eq-system-time with optimization-problem-time self.opt_solver.pop() self.opt_solver.pop() self._obligation_cache.cache(state_id, chosen_command, delta, result) self.statistics.stop_get_probability_timer() return (True, result) else: self.statistics.inc_had_to_solve_optimization_problem_counter() # for each non-target-succ, we need n opt-var opt_vars = {} # For every non-target successor, we need an optimization variable for (succ_id, prob) in succ_dist_without_target_states: opt_vars[succ_id] = Real("opt_var_%s" % succ_id) # Now assert that opt_var_i = |var_i \ (var_1 + ... + var_n) - oracle(s_i) \ ( oracle(s_1) + ... + oracle(s_n ) | # for every opt_var_i for (succ_id, prob) in succ_dist_without_target_states: # opt_var is the absolute value of the ratio self.opt_solver.add( If(((vars[succ_id] * Sum([ self.oracle.get_oracle_value(succ_id_2) for (succ_id_2, prob) in succ_dist_without_target_states ])) - ((self.oracle.get_oracle_value(succ_id) * Sum([ vars[succ_id_2] for (succ_id_2, prob) in succ_dist_without_target_states ])))) < 0, opt_vars[succ_id] == (((self.oracle.get_oracle_value(succ_id) * Sum([ vars[succ_id_2] for (succ_id_2, prob) in succ_dist_without_target_states ]))) - (vars[succ_id] * Sum([ self.oracle.get_oracle_value(succ_id_2) for (succ_id_2, prob) in succ_dist_without_target_states ]))), opt_vars[succ_id] == ((vars[succ_id] * Sum([ self.oracle.get_oracle_value(succ_id_2) for (succ_id_2, prob) in succ_dist_without_target_states ])) - ((self.oracle.get_oracle_value(succ_id) * Sum([ vars[succ_id_2] for (succ_id_2, prob) in succ_dist_without_target_states ])))))) # minimize sum of opt-vars opt = self.opt_solver.minimize( Sum([ opt_vars[succ_id] for (succ_id, prob) in succ_dist_without_target_states ])) if self.opt_solver.check() == sat: # We found probabilities or the successors m = self.opt_solver.model() result = { succ_id: m[vars[succ_id]] for (succ_id, prob) in succ_dist_without_target_states } self.opt_solver.pop() self._obligation_cache.cache(state_id, chosen_command, delta, result) self.statistics.stop_get_probability_timer() return (True, result) else: # There are no such probabilities self.opt_solver.pop() self.statistics.stop_get_probability_timer() return (False, None) def _get_probabilities_by_solving_eq_system(self, succ_dist_without_target_states, vars): self.opt_solver.push() #TODO is this correct? lhs_sum = Sum([ self.oracle.get_oracle_value(succ_id_2) for (succ_id_2, _) in succ_dist_without_target_states ]) rhs_sum = Sum([ vars[succ_id_2] for (succ_id_2, _) in succ_dist_without_target_states ]) def _multiply(left,right): args = (Ast * 2)() args[0] = left.as_ast() args[1] = right.as_ast() return ArithRef(Z3_mk_mul(left.ctx.ref(), 2, args), left.ctx) for (succ_id, prob) in succ_dist_without_target_states: # opt_var is the absolute value of the ratio lhs = _multiply(vars[succ_id], lhs_sum) rhs = _multiply(self.oracle.get_oracle_value(succ_id), rhs_sum) equality = lhs == rhs self.opt_solver.add(equality) if self.opt_solver.check() == sat: return True else: self.opt_solver.pop() return False
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)
def smt_solution(self, timeout, optimize=False): if optimize: solver = Optimize() else: solver = Solver() solver.set("zero_accuracy",10) solver.set("timeout", timeout) non_trivial = False diff_sol = False smt_keep_sol = True # a.k.a A1 smt_is_sol = True # a.k.a A2 some_consume = False some_produce = False variables = set() variables_positive = set() for p_id, place in enumerate(self.facets): ti = place.offset smt_ti = Int("b%s" % p_id) if ti: solver.add(min(0,ti) <= smt_ti, smt_ti <= max(0, ti)) else: solver.add(smt_ti == 0) facet_non_trivial = False facet_diff_sol = False smt_facet_eval = ti smt_facet_sol = ti # If halfspace's coefficients add to 1 it's # as simple as it gets simple = sum(abs(x) for x in place.normal) <= 1 # do_cons_prod indicates if the place has incoming and outgoing transitions do_cons_prod = False consume = produce = 0 for coeff in place.normal: if coeff > 0: produce = 1 elif coeff < 0: consume = 1 if consume * produce: do_cons_prod = True break do_cons_prod = reduce(lambda x,y:x*y, [x + 1 for x in place.normal]) < 1 for t_id, coeff in enumerate(place.normal): # SMT coefficient smt_coeff = Int("a%s,%s" % (p_id, t_id)) if optimize: # Try to minimize the coefficient solver.minimize(smt_coeff) # SMT variable smt_var = Int("x%s" % t_id) # Add SMT varible to the set of variables variables.add(smt_var) # Add SMT varible basic constraint to the set variables_positive.add(smt_var >= 0) if coeff: # At least one SMT coefficient should be non zero facet_non_trivial = Or(facet_non_trivial, smt_coeff != 0) # At least one SMT coefficient should be different facet_diff_sol = Or(facet_diff_sol, smt_coeff != coeff) # Original solution with SMT variable smt_facet_eval += coeff * smt_var # SMT solution with SMT variable smt_facet_sol += smt_coeff * smt_var # Keep SMT coefficient between original bundaries solver.add(min(0,coeff) <= smt_coeff, smt_coeff <= max(0, coeff)) if not self.neg_points and not simple and do_cons_prod: some_produce = Or(some_produce, smt_coeff > 0) some_consume = Or(some_consume, smt_coeff < 0) else: # Keep zero coefficients unchanged solver.add(smt_coeff == 0) if not self.neg_points: # Avoid trivial solution (i.e. all zeros as coeff) non_trivial = Or(non_trivial, facet_non_trivial) # Solution of smt must be different diff_sol = Or(diff_sol, facet_diff_sol) # If point is in old-solution should be in smt-solution smt_keep_sol = And(smt_keep_sol, smt_facet_eval <= 0) # Solutions shoul be a solution smt_is_sol = And(smt_is_sol, smt_facet_sol <= 0) if not self.neg_points and not simple and do_cons_prod: solver.add(simplify(some_consume)) solver.add(simplify(some_produce)) if optimize: # Try to minimize the independent term solver.minimize(smt_ti) # This is what we want: if not self.neg_points: # If there is not negative info, avoid trivial solution (i.e. Not all zero) solver.add(simplify(non_trivial)) # A different solution (i.e. "better") solver.add(simplify(diff_sol)) # If it was a solution before, keep it that way solver.add(simplify(ForAll(list(variables), Implies(And(Or(list(variables_positive)), smt_keep_sol), smt_is_sol)))) # Negative point shouldn't be a solution for np in self.neg_points: smt_np = False for p_id, place in enumerate(self.facets): place_at_np = Int("b%s" % p_id) for t_id, coeff in enumerate(place.normal): # If coefficient was zero, it will remain zero if coeff and np[t_id]: smt_coeff = Int("a%s,%s" % (p_id, t_id)) place_at_np = place_at_np + smt_coeff * np[t_id] smt_np = Or(smt_np, place_at_np > 0) # Keep out (NOTE halfspaces are Ax + b <= 0) solver.add(simplify(smt_np)) sol = solver.check() if sol == unsat: ret = False logger.info('Z3 returns UNSAT: Cannot reduce without adding neg info') elif sol == unknown: ret = False logger.info('Z3 returns UNKNOWN: Cannot reduce in less than %s miliseconds', timeout) else: ret = solver.model() return ret
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)))
# Bomb placement def at_least_one_bomb_for_each_rectangle(h, w): return [ PbGe([(is_bomb[r + dr][c + dc], 1) for (dr, dc) in rectangle(h, w)], 1) for (r, c) in rectangle(H - h + 1, W - w + 1) ] # Clauses s = Optimize() s.add(at_least_one_bomb_for_each_rectangle(2, 3)) s.add(at_least_one_bomb_for_each_rectangle(3, 2)) # Objective num_bombs = Sum([If(is_bomb[r][c], 1, 0) for (r, c) in rectangle(H, W)]) min_bombs = s.minimize(num_bombs) if s.check() == sat: assert s.lower(min_bombs) == 6 print("The minimum number of bombs satisfying the constraints == %s." % s.lower(min_bombs)) print(diagram(s.model())) else: print("Z3 failed to find a solution.") # http://forum.stratego.com/topic/1134-stratego-quizz-and-training-forum/?p=11661 # http://forum.stratego.com/topic/1146-stratego-quizz-and-training-forum-answers/?p=11813 # http://forum.stratego.com/topic/1134-stratego-quizz-and-training-forum/?p=441745 print( "The minimum number of bombs on a Stratego setup area such that each 2x3, 3x2 and 1x6 rectangle has at least one bomb." )
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": z3.minimize(depth) satisfiable = z3.check() if satisfiable == sat: not_solve = False else: T = int(1.3 * T) print("Compilation time = {}s.".format(sys_time.time() - start_time)) model = z3.model() if objective_name == "fidelity": log_fidelity = model[fidelity].as_long() print("result fidelity = {}".format(math.exp(log_fidelity / 1000.0))) for k in range(K):
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))
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 else: T += 1 print("Compilation time = {}.".format(sys_time.time() - start_time)) model = z3.model() if objective_name == "fidelity": log_fidelity = model[fidelity].as_long() print("result fidelity = {}".format(math.exp(log_fidelity / 1000.0)))
def smt_solution(self, timeout, neg_points=None, optimize=False): # If halfspace's coefficients add to 1 it's # as simple as it gets normal_sum = sum(abs(x) for x in self.normal) if normal_sum == 0: return False elif normal_sum <= 1: simple = True else: simple = False neg_points = neg_points or [] if optimize: solver = Optimize() else: solver = Solver() solver.set("zero_accuracy",10) solver.set("timeout", timeout) ti = self.offset smt_ti = Int("b") if ti: solver.add(min(0,ti) <= smt_ti, smt_ti <= max(0,ti)) if optimize: # Try to minimize the independent term solver.minimize(smt_ti) else: solver.add(smt_ti == 0) variables = set() variables_positive = set() positive_x = True non_trivial = False some_consume = False some_produce = False diff_sol = smt_ti != ti smt_keep_sol = ti smt_is_sol = smt_ti for t_id, coeff in enumerate(self.normal): # SMT coefficient smt_coeff = Int("a%s" % t_id) if optimize: # Try to minimize the coefficient solver.minimize(smt_coeff) # SMT variable smt_var = Int("x%s"%t_id) # Add SMT varible to the set of variables variables.add(smt_var) # Add SMT varible basic constraint to the set variables_positive.add(smt_var >= 0) if coeff: # At least one SMT coefficient should be non zero non_trivial = Or(non_trivial, smt_coeff != 0) # At least one SMT coefficient should be different diff_sol = Or(diff_sol, smt_coeff != coeff) # Original solution with SMT variable smt_keep_sol += coeff * smt_var # SMT solution with SMT variable smt_is_sol += smt_coeff * smt_var # Keep SMT coefficient between original bundaries solver.add(min(0,coeff) <= smt_coeff, smt_coeff <= max(0, coeff)) if not neg_points and not simple: some_produce = Or(some_produce, smt_coeff > 0) some_consume = Or(some_consume, smt_coeff < 0) else: solver.add(smt_coeff == 0) #This is what we want: if not neg_points: # If there is not negative info, avoid trivial solution (i.e. Not all zero) solver.add(simplify(non_trivial)) if not simple: solver.add(simplify(some_consume)) solver.add(simplify(some_produce)) # A different solution (i.e. "better") solver.add(simplify(diff_sol)) # If it was a solution before, keep it that way solver.add(simplify(ForAll(list(variables), Implies(And(Or(list(variables_positive)), smt_keep_sol <= 0), smt_is_sol <= 0)))) # New solution shouldn't add a negative point for np in neg_points: smt_ineq_np = smt_ti ineq_np = ti for t_id, coeff in enumerate(self.normal): ineq_np += coeff * np[t_id] smt_coeff = Int("a%s"%(t_id)) smt_ineq_np += smt_coeff * np[t_id] # If neg point was out of this halfspace, keep it out! if ineq_np > 0: logger.info('Adding HS SMT-NEG restriction') solver.add(simplify(smt_ineq_np > 0)) sol = solver.check() if sol == unsat: ret = False logger.info('Z3 returns UNSAT: Cannot simplify HS without adding neg info') elif sol == unknown: ret = False logger.info('Z3 returns UNKNOWN: Cannot simplify HS in less than %s miliseconds', timeout) else: ret = solver.model() return ret