def search_rectangle(b1, e1, b2, e2, b3, e3, b4, e4): x1 = Real('x1') x2 = Real('x2') x3 = Real('x3') x4 = Real('x4') y1 = Real('y1') y2 = Real('y2') y3 = Real('y3') y4 = Real('y4') optimizer = Optimize() add_cond_on_segment(optimizer, b1, e1, x1, y1) add_cond_on_segment(optimizer, b2, e2, x2, y2) add_cond_on_segment(optimizer, b3, e3, x3, y3) add_cond_on_segment(optimizer, b4, e4, x4, y4) add_cond_orthogonal(optimizer, x1, y1, x2, y2, x3, y3) add_cond_orthogonal(optimizer, x2, y2, x3, y3, x4, y4) add_cond_orthogonal(optimizer, x3, y3, x4, y4, x1, y1) add_cond_orthogonal(optimizer, x4, y4, x1, y1, x2, y2) # equal_distance #optimizer.add_soft((x2-x1)**2+(y2-y1)**2==(x4-x3)**2+(y4-y3)**2) #optimizer.add_soft((x3-x2)**2+(y3-y2)**2==(x4-x1)**2+(y4-y1)**2) #optimizer.maximize(z3abs((x2-x1)*(y4-y1)-(x4-x1)*(y2-y1))) result = optimizer.check() print(result) fig = plt.figure() ax = fig.add_subplot(111) plotline(ax, b1, e1) plotline(ax, b2, e2) plotline(ax, b3, e3) plotline(ax, b4, e4) if result != z3.unsat: print(optimizer.model()) model = optimizer.model() x1 = convert_py_type(model[x1]) x2 = convert_py_type(model[x2]) x3 = convert_py_type(model[x3]) x4 = convert_py_type(model[x4]) y1 = convert_py_type(model[y1]) y2 = convert_py_type(model[y2]) y3 = convert_py_type(model[y3]) y4 = convert_py_type(model[y4]) plotline(ax, [x1, y1], [x2, y2], 'k') plotline(ax, [x2, y2], [x3, y3], 'k') plotline(ax, [x3, y3], [x4, y4], 'k') plotline(ax, [x4, y4], [x1, y1], 'k') else: print('no soltion') plt.gca().set_aspect('equal', adjustable='box') plt.show()
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 __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()
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 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 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 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 test_allBadSpecsAreBad(spec): badSpecs = getAllBadSpecs(spec, 15) for badSpec in badSpecs: foundry = Foundry( Optimize()).applyAndTrackSpec(badSpec).applyAndTrackSpec(spec) assert foundry.check() == unsat assert len(foundry.getReasonsForUnsat()) != 0
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 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
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 bruteForceRepairLine(broken: List[Pitch], spec: Spec) -> List[int]: foundry = Foundry(Optimize()) foundry.applySpec(spec) for fixedNoteIndexes in revpowset(range(0, len(broken))): foundry.opt.push() for i in fixedNoteIndexes: foundry.apply( Constraint(spec.line[i] == broken[i], ConstraintType.REPAIRER, "Ignore")) if foundry.check() == sat: return foundry.extractPitches(spec.line) foundry.opt.pop() raise Exception("No valid line could be found")
def check_with_stats(solver: z3.Optimize) -> z3.CheckSatResult: start: float = time.perf_counter() SchedulingStatistics.__instance.number_smt_calls += 1 r = solver.check() solving_time = time.perf_counter() - start if r == z3.sat: SchedulingStatistics.__instance.number_sat_results += 1 SchedulingStatistics.__instance.sat_solving_time += solving_time elif r == z3.unsat: SchedulingStatistics.__instance.number_unsat_results += 1 SchedulingStatistics.__instance.unsat_solving_time += solving_time else: assert False return r
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 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 create_optimizer(): s = Optimize() input = Ints(' '.join(map(lambda s: str(s), range(14)))) for i in input: s.add(i > 0, i < 10) data = [ (1, 11, 16), (1, 12, 11), (1, 13, 12), (26, -5, 12), (26, -3, 12), (1, 14, 2), (1, 15, 11), (26, -16, 4), (1, 14, 12), (1, 15, 9), (26, -7, 10), (26, -11, 11), (26, -6, 6), (26, -11, 15), ] x, y, z = 0, 0, 0 for (zz, b, c), i in zip(data, input): w = i x = z % 26 z /= zz x += b x = x != w y = 25 y *= x y += 1 z *= y y = w + c y *= x z += y s.add(z == 0) num = 0 for i in input: num *= 10 num += i return s, num
def getAllBadSpecs(spec: Spec, maxCount=None, doShuffle=True, filterOutUnsatisfiable=True) -> List[Spec]: badSpecs = [] constraintsToInclude = powset(spec.constraints) constraintsToInvert = constraintsToInclude[::-1] constraintsToInclude = constraintsToInclude[:-1] constraintsToInvert = constraintsToInvert[:-1] if doShuffle: constraintsToInclude, constraintsToInvert = shuffle( constraintsToInclude, constraintsToInvert) if maxCount is None: maxCount = len(constraintsToInclude) else: maxCount = min(maxCount, len(constraintsToInclude)) for i in range(maxCount): constraints = [ pitchesLetterValueValid(spec.line) ] # Needed ensure that the spec doesn't cheat on the gamut constraint constraints += constraintsToInclude[i] constraints += [x.inv() for x in constraintsToInvert[i]] badSpec = Spec(spec.line, constraints, spec.maximisations, spec.minimisations) foundry = Foundry(Optimize()).applySpec(badSpec) if filterOutUnsatisfiable: if foundry.check() == sat: badSpecs.append(badSpec) else: badSpecs.append(badSpec) return badSpecs
def solve_pkgver_z3(pkg_dict, constrain_version_dict, optim_target="approximate"): # solver = Solver() solver = Optimize() int_dict = build_int_dict(pkg_dict) order_dict = build_order_dict(pkg_dict) solver = add_dependency_constrains(solver, pkg_dict, order_dict, int_dict) solver = add_int_constrains(solver, pkg_dict, int_dict) solver = add_version_constrains(solver, constrain_version_dict, order_dict, int_dict) if optim_target == "approximate": solver = add_optimize_targets(solver, int_dict) if optim_target == "exact": solver = add_optimize_targets2(solver, int_dict, order_dict) solver.set(timeout=60000) try: if solver.check(): pkgvers = parse_z3_model(solver.model(), int_dict, order_dict) return pkgvers else: return None except: return None
num_swap = Int('num_swap') # for fidelity optimization if objective_name == "fidelity": u = [Int("num_1qbg_p{}".format(n)) for n in range(N)] v = [Int("num_2qbg_e{}".format(k)) for k in range(K)] vv = [Int("num_swap_e{}".format(k)) for k in range(K)] w = [Int("num_meas_p{}".format(n)) for n in range(N)] fidelity = Int('log_fidelity') # else: # for depth optimization depth = Int('depth') z3 = Optimize() """ Constraints """ for t in range(T): for m in range(M): z3.add(pi[m][t] >= 0, pi[m][t] < N) for mm in range(m): z3.add(pi[m][t] != pi[mm][t]) for l in range(L): z3.add(time[l] >= 0, time[l] < T) if l in G_1:
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
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
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)
# Variables is_bomb = np.array([ Bool("is_bomb_%s%s" % (r, c)) for (r, c) in rectangle(H, W) ]).reshape(H, W).tolist() # 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.")
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
import time import csv from z3 import Optimize from z3 import * # create a dictionary contains all the street name variables street = {} # create a list of list contains all the intersections intersects = [] # track increament variable i = 0 # Optimize API solver objective functions opt = Optimize() # adding integer variables X = [] # adding constrain for each street variable C = [] # read from the .csv file with open("../Dataset/intersection_lane.csv") as f: reader = csv.reader(f, delimiter=",") # for each row in file: for row in reader: st1 = row[0] # create two new variable STREET1, STREET2 if not st1 in street:
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 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
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
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 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 )
def from_model(model): start_over() context = Context() context.load_metamodel() context.load_model(model) solver = Optimize() generate_meta_constraints() generate_config_constraints() solver.add(*get_all_meta_facts()) solver.add(*get_all_config_facts()) context.declare(INTEGRITY_VARIABLES) context.declare_helper_functions() for each_constraint in INTEGRITY_CONSTRAINTS: solver.add(context.evaluate(each_constraint)) for each_running_service in model.goals.services: constraint = RUNNING_SERVICE.format(each_running_service.name) solver.add(context.evaluate(constraint)) for each_constraint in model.constraints: solver.add(context.evaluate(each_constraint)) for each_constraint in context.value_constraints: solver.add(context.evaluate(each_constraint)) #print solver.sexpr() return Z3Problem(model, context, solver)
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())