def snakes_find_the_head(g): # We'll want a type that represents a cell in the grid. We use a datatype because we can make it a sum type - # that is, its 0-arity constructors represent a closed enumeration of distinct objects. Cell = Datatype("Cell") for x, y in g.coords(): if g.snake(x, y) is not None: Cell.declare("cell_{}_{}".format(x, y)) Cell = Cell.create() # We'll have two functions that we explicitly declare. One is the short-distance "connected" relationship. # The other is a convenience function for turning the coordinates of a cell into the datatype member that # represents that position. Connected = Function("Connected", Cell, Cell, BoolSort()) XYToCell = Function("XYToCell", IntSort(), IntSort(), Cell) cell = {} for x, y in g.coords(): if g.snake(x, y) is not None: cell[x, y] = getattr(Cell, "cell_{}_{}".format(x, y)) g.add(XYToCell(x, y) == cell[x, y]) # Two cells are connected *if and only* if they are adjacent and both hold snakes # We need to be really clear about the "and only if" part; a naive implementation here will let the # solver fill in other arbitrary values for `Connected` in order to make the desired outcome true. # We do this by ensuring there's a value declared for our Connected relationship between every pair # of potential arguments. for x1, y1 in g.coords(): c1 = g.snake(x1, y1) if c1 is None: continue # If there's a snake here, the cell is connected to itself g.add(Connected(cell[x1, y1], cell[x1, y1]) == c1) for x2, y2 in g.coords(): c2 = g.snake(x2, y2) if c2 is None or (x1, y1) == (x2, y2): continue if manh(x1, y1, x2, y2) == 1: g.add(Connected(cell[x1, y1], cell[x2, y2]) == And(c1, c2)) else: # Without this, our function declaration is only partial. The solver can fill in missing values # in order to scupper our good intentions. g.add(Not(Connected(cell[x1, y1], cell[x2, y2]))) # The transitive closure of Connectedness is Reaches Reaches = TransitiveClosure(Connected) # For every cell in the grid, if it's a snake then we can connect it to the head position hx, hy = g.head() for x, y in g.coords(): c = g.snake(x, y) if c is None: continue g.add(Implies(c, Reaches(cell[x, y], XYToCell(hx, hy))))
def sort(x): if type(x) == bool: return BoolSort() elif type(x) == int: return IntSort() elif type(x) == float: return RealSort() elif hasattr(x, 'sort'): if x.sort() == BoolSort(): return BoolSort() if x.sort() == IntSort(): return IntSort() if x.sort() == RealSort(): return RealSort() else: raise Exception('unknown sort for expression')
def main() -> None: schema = {"a": IntSort(), "b": IntSort()} constraints = [ SelectProjectQuery(["a"], lambda a, b: a * b == 0), SelectProjectQuery(["b"], lambda a, b: a == 42), ] query = SelectProjectQuery(["b"], lambda a, b: Or(And(Or(a == 0, b == 0), a > 0), And(a == b * (b + 1), b == 6))) print(check_privacy(schema, constraints, query))
def ISZERO(self): op0 = self._stack_pop() if type(op0) is int: if op0 == 0: self._stack_push(Word(1)) else: self._stack_push(Word(0)) elif type(op0) is BoolRef: self._stack_push(IntSort().cast(Not(op0))) else: self._stack_push(IntSort().cast(op0 == 0))
def main(): """Skyscraper solver example.""" lattice = grilops.get_square_lattice(SIZE) directions = {d.name: d for d in lattice.edge_sharing_directions()} sg = grilops.SymbolGrid(lattice, SYM) # Each row and each column contains each building height exactly once. for y in range(SIZE): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)])) for x in range(SIZE): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)])) # We'll use the sightlines accumulator to keep track of a tuple storing: # the tallest building we've seen so far # the number of visible buildings we've encountered Acc = Datatype("Acc") # pylint: disable=C0103 Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort())) Acc = Acc.create() # pylint: disable=C0103 def accumulate(a, height): return Acc.acc( If(height > Acc.tallest(a), height, Acc.tallest(a)), If(height > Acc.tallest(a), Acc.num_visible(a) + 1, Acc.num_visible(a))) for x, c in enumerate(GIVEN_TOP): sg.solver.add(c == Acc.num_visible( grilops.sightlines.reduce_cells(sg, Point(0, x), directions["S"], Acc.acc(0, 0), accumulate))) for y, c in enumerate(GIVEN_LEFT): sg.solver.add(c == Acc.num_visible( grilops.sightlines.reduce_cells(sg, Point(y, 0), directions["E"], Acc.acc(0, 0), accumulate))) for y, c in enumerate(GIVEN_RIGHT): sg.solver.add(c == Acc.num_visible( grilops.sightlines.reduce_cells(sg, Point( y, SIZE - 1), directions["W"], Acc.acc(0, 0), accumulate))) for x, c in enumerate(GIVEN_BOTTOM): sg.solver.add(c == Acc.num_visible( grilops.sightlines.reduce_cells(sg, Point( SIZE - 1, x), directions["N"], Acc.acc(0, 0), accumulate))) if sg.solve(): sg.print() print() if sg.is_unique(): print("Unique solution") else: print("Alternate solution") sg.print() else: print("No solution")
def no_intersections(belt1: Belt, belt2: Belt): assert isinstance(belt1, Belt) assert isinstance(belt2, Belt) i,j = Consts("i j", IntSort()) return ForAll([i,j], Implies(And(0 <= i, i < belt1.belt_len, 0 <=j, j < belt2.belt_len), Not(belt1[i] == belt2[j])))
def non_intersecting_seg_belts(belt1: SegmentedBelt , belt2: SegmentedBelt): assert isinstance(belt1, SegmentedBelt) assert isinstance(belt2, SegmentedBelt) i,j = Consts("i j", IntSort()) return ForAll([i,j], Implies(And(0 <= i, i < belt1.num_segs, 0 <=j, j < belt2.num_segs), non_intersecting_segs(belt1.segment(i), belt2.segment(j))))
def EQ(self): op0 = self._stack_pop() op1 = self._stack_pop() try: self._stack_push(IntSort().cast(op0 == op1)) except Z3Exception as e: print(e)
def skyscraper(givens: Dict[Direction, List[int]]) -> str: """Solver for Skyscraper minipuzzles.""" sym = grilops.make_number_range_symbol_set(1, SIZE) sg = grilops.SymbolGrid(LATTICE, sym) shifter = Shifter(sg.solver) # Each row and each column contains each building height exactly once. for y in range(SIZE): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)])) for x in range(SIZE): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)])) # We'll use the sightlines accumulator to keep track of a tuple storing: # the tallest building we've seen so far # the number of visible buildings we've encountered Acc = Datatype("Acc") # pylint: disable=C0103 Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort())) Acc = Acc.create() # pylint: disable=C0103 def accumulate(a, height): return Acc.acc( If(height > Acc.tallest(a), height, Acc.tallest(a)), If(height > Acc.tallest(a), Acc.num_visible(a) + 1, Acc.num_visible(a))) for d, gs in givens.items(): for i, g in enumerate(gs): if d.vector.dy != 0: g = g - shifter.col_shifts.get(i, 0) p = Point(0 if d.vector.dy < 0 else SIZE - 1, i) elif d.vector.dx != 0: g = g - shifter.row_shifts.get(i, 0) p = Point(i, 0 if d.vector.dx < 0 else SIZE - 1) sg.solver.add(g == Acc.num_visible( # type: ignore[attr-defined] grilops.sightlines.reduce_cells( sg, p, LATTICE.opposite_direction(d), Acc.acc(0, 0), # type: ignore[attr-defined] accumulate))) assert sg.solve() sg.print() print() shifter.print_shifts() print() return shifter.eval_binary()
def main(): dim = int(input("size of the array: ")) i = [Int(f"i_{x}") for x in range(dim + 1)] j = [Int(f"j_{x}") for x in range(dim + 1)] A = [Array(f"A_{x}", IntSort(), IntSort()) for x in range(dim + 1)] tmp = [Int(f"tmp_{x}") for x in range(dim)] s = Solver() init_condition = init(i[0], j[0]) s.add(init_condition) tran_condition = mk_tran_condition(A, i, j, tmp, dim) s.add(And(*tran_condition)) values = [Int(f"n_{x}") for x in range(dim)] init_check_condition = check(values, A[-1], dim) s.add(And(*init_check_condition)) post_condition = mk_post_condition(values) print("Bubble sort") print("---------------------") s.push() s.add(Not(post_condition)) print("Testing the validity of the algorithm; `valid expected`:") if s.check() == sat: print(f"counterexample:\n{s.model()}") else: print("valid") print("---------------------") s.pop() s.add(post_condition) print("Getting a model...") print("Model:") if s.check() == sat: print(s.model())
def test_datatype_payloads(self): lattice = get_square_lattice(3) sym = make_number_range_symbol_set(0, 2) row_grid = SymbolGrid(lattice, sym) col_grid = SymbolGrid(lattice, sym, solver=row_grid.solver) RowCol = Datatype("RowCol") RowCol.declare("row_col", ("row", IntSort()), ("col", IntSort())) RowCol = RowCol.create() sc = ShapeConstrainer(lattice, [ Shape([ (Vector(0, 0), RowCol.row_col(0, 0)), (Vector(0, 1), RowCol.row_col(0, 1)), (Vector(1, 0), RowCol.row_col(1, 0)), ]), Shape([ (Vector(0, 1), RowCol.row_col(0, 2)), (Vector(1, 0), RowCol.row_col(1, 1)), (Vector(1, 1), RowCol.row_col(1, 2)), (Vector(2, 1), RowCol.row_col(2, 2)), ]), Shape([ (Vector(0, 0), RowCol.row_col(2, 0)), (Vector(0, 1), RowCol.row_col(2, 1)), ]), ], solver=row_grid.solver, complete=True) for p in lattice.points: row_grid.solver.add( row_grid.cell_is(p, RowCol.row(sc.shape_payload_grid[p]))) col_grid.solver.add( col_grid.cell_is(p, RowCol.col(sc.shape_payload_grid[p]))) self.assertTrue(row_grid.solve()) solved_row_grid = row_grid.solved_grid() solved_col_grid = col_grid.solved_grid() for p in lattice.points: self.assertEqual(solved_row_grid[p], p.y) self.assertEqual(solved_col_grid[p], p.x) self.assertTrue(row_grid.is_unique())
def GetStringFromType(typ): srt = GetSortFromType(typ) if srt == IntSort(): return 'Int' elif srt == BoolSort(): return 'Bool' elif is_bv_sort(srt): sz = srt.size() return '(BitVec ' + str(sz) + ')' else: raise SynthException('Unknonw sort for string conversion.')
def GetValFromType(typ, raw_val): srt = GetSortFromType(typ) if srt == IntSort(): return IntVal(raw_val) elif srt == BoolSort(): return BoolVal(raw_val) elif is_bv_sort(srt): sz = srt.size() return BitVecVal(raw_val, sz) else: raise SynthException('Unknown sort')
def add_observation_constraints(self, s, planner, ground_actions, length, observations): obsSort = DeclareSort('Obs') orderObs = Function('orderObs', obsSort, IntSort()) orderExec = Function('orderExec', obsSort, IntSort()) obsConsts = [] for i in range(0, len(observations)): o = Const(str(observations[i]), obsSort) obsConsts.append(o) s.add(orderObs(o) == i) for t in range(0, length): # forced_obs = [] for action in ground_actions: index = observations.index_of(action.signature()) if index > -1: obsC = obsConsts[index] # forced_obs.append(planner.action_prop_at(action, t)) s.add( Implies(planner.action_prop_at(action, t), orderExec(obsC) == t)) # s.add(Or(*forced_obs)) x = Const('x', obsSort) y = Const('y', obsSort) # orderSync = Function('order-sync', BoolSort()) s.add( ForAll([x, y], Implies( orderObs(x) < orderObs(y), orderExec(x) < orderExec(y)))) s.add( ForAll([x, y], Implies( orderObs(x) == orderObs(y), orderExec(x) == orderExec(y)))) s.add( ForAll([x, y], Implies( orderObs(x) > orderObs(y), orderExec(x) > orderExec(y))))
def rational_to_acl2(r): r = simplify(r) if (r.sort() == IntSort()): return (integer_to_acl2(r)) elif ((r.sort() == RealSort()) and (r.decl().name() == 'Real')): if (r.denominator_as_long() == 1): return r.numerator().as_string() else: return ('(/ ' + r.numerator().as_string() + ' ' + r.denominator().as_string() + ')') else: raise Exception('rational_to_acl2, badarg: ' + str(r))
def non_intersecting_seg_belt_diag_seg(belt: SegmentedBelt, dseg: Segment): """ all segments of a belt must not intersect a given ordered diagonal segment """ assert isinstance(belt, SegmentedBelt) assert isinstance(dseg, Segment) assert dseg.is_diag i = Const("i", IntSort()) return ForAll([i], Implies(And(0 <= i, i < belt.num_segs), Or(Max(belt.segment(i).p1.x, belt.segment(i).p2.x) < dseg.p1.x, Min(belt.segment(i).p1.x, belt.segment(i).p2.x) > dseg.p2.x, Max(belt.segment(i).p1.y, belt.segment(i).p2.y) < dseg.p1.y, Min(belt.segment(i).p1.y, belt.segment(i).p2.y) > dseg.p2.y)))
def GetSortFromType(typ): if typ == 'Int': return IntSort() elif typ == 'Bool': return BoolSort() elif isinstance(typ, list): if (len(typ) != 2 or typ[0] != 'BitVec'): raise SynthException('Unknown Type %r' % (typ)) else: intName, size = typ[1] return BitVecSort(size) else: raise SynthException('Unknown Type %r' % (typ))
def quantified(self): """ Returns a list of Z3 variables, one for each parameter of this type. """ if self._qf is not None: return self._qf res = [] if isinstance(self.name, tuple): for i, arg in enumerate(self.name[1:]): sort = self.type_sort if not arg.endswith( 'defaults_args') else IntSort() cur = Const("y" + str(i), sort) res.append(cur) self._qf = res return res
def __init__(self, max_segs=None): self.corners_x = Function(f'seg_belt{self._IDX}_x', IntSort(), IntSort()) self.corners_y = Function(f'seg_belt{self._IDX}_y', IntSort(), IntSort()) self.num_segs = Const(f'seg_belt{self._IDX}_num_segs', IntSort()) SOL.add(self.num_segs > 0) if max_segs: SOL.add(self.num_segs <= max_segs) i,j = Consts("i j", IntSort()) SOL.add(ForAll([i], Implies(And(0 <= i, i < self.num_segs), Or(self.segment(i).horizontal(), self.segment(i).vertical())))) self.__class__._IDX += 1
def __init__(self): self.belt_x = Function(f'belt{self._IDX}_x', IntSort(), IntSort()) self.belt_y = Function(f'belt{self._IDX}_y', IntSort(), IntSort()) self.belt_len = Const(f'belt{self._IDX}_len', IntSort()) SOL.add(self.belt_len > 0) i,j = Consts("i j", IntSort()) # neighbor condition SOL.add(ForAll([i], Implies(And(i < self.belt_len - 1, i >= 0), neighs(self[i], self[i+1])))) # no intersections SOL.add(ForAll([i, j], Implies(And(i >= 0, i < j, j < self.belt_len), Not(self[i] == self[j])))) self.__class__._IDX += 1
def __create_grids(self): """Create the grids used to model shape region constraints.""" self.__shape_type_grid: Dict[Point, ArithRef] = {} for p in self.__lattice.points: v = Int(f"scst-{ShapeConstrainer._instance_index}-{p.y}-{p.x}") if self.__complete: self.__solver.add(v >= 0) else: self.__solver.add(v >= -1) self.__solver.add(v < len(self.__shapes)) self.__shape_type_grid[p] = v self.__shape_instance_grid: Dict[Point, ArithRef] = {} for p in self.__lattice.points: v = Int(f"scsi-{ShapeConstrainer._instance_index}-{p.y}-{p.x}") if self.__complete: self.__solver.add(v >= 0) else: self.__solver.add(v >= -1) self.__solver.add(v < len(self.__lattice.points)) self.__shape_instance_grid[p] = v sample_payload = self.__shapes[0].offsets_with_payloads[0][1] if sample_payload is None: self.__shape_payload_grid: Optional[Dict[Point, Payload]] = None else: self.__shape_payload_grid: Optional[Dict[Point, Payload]] = {} if isinstance(sample_payload, ExprRef): sort = sample_payload.sort() elif isinstance(sample_payload, int): sort = IntSort() else: raise Exception( f"Could not determine z3 sort for {sample_payload}") for p in self.__lattice.points: v = Const( f"scsp-{ShapeConstrainer._instance_index}-{p.y}-{p.x}", sort) self.__shape_payload_grid[p] = v
def GT(self): op0 = self._stack_pop() op1 = self._stack_pop() self._stack_push(IntSort().cast(op0 > op1))
def integerp(self, x): return sort(x) == IntSort()
def __init__( self, problem, debug: Optional[bool] = False, max_time: Optional[int] = 10, parallel: Optional[bool] = False, random_values: Optional[bool] = False, logics: Optional[str] = None, verbosity: Optional[int] = 0, ): """Scheduling Solver debug: True or False, False by default max_time: time in seconds, 60 by default parallel: True to enable mutlthreading, False by default """ self.problem = problem self.problem_context = problem.context self.debug = debug # objectives list self.objective = None # the list of all objectives defined in this problem self.current_solution = None # no solution until the problem is solved # set_option('smt.arith.auto_config_simplex', True) if debug: set_option("verbose", 2) else: set_option("verbose", verbosity) if random_values: set_option("sat.random_seed", random.randint(1, 1e3)) set_option("smt.random_seed", random.randint(1, 1e3)) set_option("smt.arith.random_initial_value", True) else: set_option("sat.random_seed", 0) set_option("smt.random_seed", 0) set_option("smt.arith.random_initial_value", False) # set timeout self.max_time = max_time # in seconds set_option("timeout", int(self.max_time * 1000)) # in milliseconds # create the solver print("Solver type:\n===========") # check if the problem is an optimization problem self.is_not_optimization_problem = len( self.problem_context.objectives) == 0 self.is_optimization_problem = len(self.problem_context.objectives) > 0 self.is_multi_objective_optimization_problem = (len( self.problem_context.objectives) > 1) # the Optimize() solver is used only in the case of a mutli-optimization # problem. This enables to choose the priority method. # in the case of a single objective optimization, the Optimize() solver # apperas to be less robust than the basic Solver(). The # incremental solver is then used. # see this url for a documentation about logics # http://smtlib.cs.uiowa.edu/logics.shtml if logics is None: self._solver = Solver() print("\t-> Standard SAT/SMT solver") else: self._solver = SolverFor(logics) print("\t-> SMT solver using logics", logics) if debug: set_option(unsat_core=True) if parallel: set_option("parallel.enable", True) # enable parallel computation # add all tasks assertions to the solver for task in self.problem_context.tasks: self.add_constraint(task.get_assertions()) self.add_constraint(task.end <= self.problem.horizon) # then process tasks constraints for constraint in self.problem_context.constraints: self.add_constraint(constraint) # process resources requirements for ress in self.problem_context.resources: self.add_constraint(ress.get_assertions()) # process resource intervals for ress in self.problem_context.resources: busy_intervals = ress.get_busy_intervals() nb_intervals = len(busy_intervals) for i in range(nb_intervals): start_task_i, end_task_i = busy_intervals[i] for k in range(i + 1, nb_intervals): start_task_k, end_task_k = busy_intervals[k] self.add_constraint( Or(start_task_k >= end_task_i, start_task_i >= end_task_k)) # process indicators for indic in self.problem_context.indicators: self.add_constraint(indic.get_assertions()) # work amounts # for each task, compute the total work for all required resources""" for task in self.problem_context.tasks: if task.work_amount > 0.0: work_total_for_all_resources = [] for required_resource in task.required_resources: # work contribution for the resource interv_low, interv_up = required_resource.busy_intervals[ task] work_contribution = required_resource.productivity * ( interv_up - interv_low) work_total_for_all_resources.append(work_contribution) self.add_constraint( Sum(work_total_for_all_resources) >= task.work_amount) # process buffers for buffer in self.problem_context.buffers: # # create an array that stores the mapping between start times and # quantities. For example, if a start T1 starts at 2 and consumes # 8, and T3 ends at 6 and consumes 5 then the mapping array # will look like : A[2]=8 and A[6]=-5 # SO far, no way to have the same start time at different inst buffer_mapping = Array("Buffer_%s_mapping" % buffer.name, IntSort(), IntSort()) for t in buffer.unloading_tasks: self.add_constraint(buffer_mapping == Store( buffer_mapping, t.start, -buffer.unloading_tasks[t])) for t in buffer.loading_tasks: self.add_constraint(buffer_mapping == Store( buffer_mapping, t.end, +buffer.loading_tasks[t])) # sort consume/feed times in asc order tasks_start_unload = [t.start for t in buffer.unloading_tasks] tasks_end_load = [t.end for t in buffer.loading_tasks] sorted_times, sort_assertions = sort_no_duplicates( tasks_start_unload + tasks_end_load) self.add_constraint(sort_assertions) # create as many buffer state changes as sorted_times buffer.state_changes_time = [ Int("%s_sc_time_%i" % (buffer.name, k)) for k in range(len(sorted_times)) ] # add the constraints that give the buffer state change times for st, bfst in zip(sorted_times, buffer.state_changes_time): self.add_constraint(st == bfst) # compute the different buffer states according to state changes buffer.buffer_states = [ Int("%s_state_%i" % (buffer.name, k)) for k in range(len(buffer.state_changes_time) + 1) ] # add constraints for buffer states # the first buffer state is equal to the buffer initial level if buffer.initial_state is not None: self.add_constraint( buffer.buffer_states[0] == buffer.initial_state) if buffer.final_state is not None: self.add_constraint( buffer.buffer_states[-1] == buffer.final_state) if buffer.lower_bound is not None: for st in buffer.buffer_states: self.add_constraint(st >= buffer.lower_bound) if buffer.upper_bound is not None: for st in buffer.buffer_states: self.add_constraint(st <= buffer.upper_bound) # and, for the other, the buffer state i+1 is the buffer state i +/- the buffer change for i in range(len(buffer.buffer_states) - 1): self.add_constraint( buffer.buffer_states[i + 1] == buffer.buffer_states[i] + buffer_mapping[buffer.state_changes_time[i]]) # optimization if self.is_optimization_problem: self.create_objective()
for e in range(dim): yield variables[e] == Select(Ar, e) def mk_post_condition(values): condition = [] for v1, v2 in zip(values, values[1:]): condition.append(v1 <= v2) return And(*condition) dim = int(input("size of the array: ")) i = [Int(f"i_{x}") for x in range(dim + 1)] j = [Int(f"j_{x}") for x in range(dim + 1)] A = [Array(f"A_{x}", IntSort(), IntSort()) for x in range(dim + 1)] tmp = [Int(f"tmp_{x}") for x in range(dim)] s = Solver() init_condition = init(i[0], j[0]) s.add(init_condition) tran_condition = mk_tran_condition(A, i, j, tmp, dim) s.add(And(*tran_condition)) values = [Int(f"n_{x}") for x in range(dim)] init_check_condition = check(values, A[-1], dim) s.add(And(*init_check_condition)) post_condition = mk_post_condition(values)
def __init__(self, x=None, y=None): x = Const(f"p{self._IDX}_x", IntSort()) if x is None else x y = Const(f"p{self._IDX}_y", IntSort()) if y is None else y self.__class__._IDX += 1 super().__init__(x, y)
from z3 import Not, And, ForAll, Implies, IntSort, BoolSort, Solver, Function, Int __counter = 100 __agents = {} def new_noun(name): global __counter global __agents if name not in __agents: __agents[name] = __counter __counter += 1 return Int(__agents[name]) IsKinase = Function("is_kinase", IntSort(), BoolSort()) IsActiveWhenPhosphorylated = Function("is_active_when_phosphorylated", IntSort(), BoolSort()) Phosphorylates = Function("phosphorylates", IntSort(), IntSort(), BoolSort()) Activates = Function("activates", IntSort(), IntSort(), BoolSort()) ActivityIncreasesActivity = Function("activity_increases_activity", IntSort(), IntSort(), BoolSort()) solver = Solver() MEK1 = new_noun("MEK1") ERK1 = new_noun("ERK1") RAF = new_noun("RAF") HRAS = new_noun("HRAS")
"""Z3 implementation of L - the meta-Kappa devised by Adrien Husson and Jean Krivine - using the Python bindings for Z3. """ from z3 import Datatype, ArraySort, IntSort, BoolSort, Function, Const from z3_helpers import Iff, Equals # Node is a datatype representing a vertex or node in a Kappa graph. Node = Datatype('Node') Node.declare('node', ('unique_identifier', IntSort())) Node = Node.create() # A datatype for storing a pair of edges Edge = Datatype('Edge') Edge.declare('edge', ('node1', Node), ('node2', Node)) Edge = Edge.create() Nodeset = ArraySort(Node, BoolSort()) Edgeset = ArraySort(Edge, BoolSort()) Labelset = ArraySort(IntSort(), BoolSort()) Labelmap = ArraySort(Node, Labelset) # Graph, before a rule or action has applied. Merged Pregraph and Postgraph # into a single datatype. Graph = Datatype('Graph') Graph.declare('graph', ('has', Nodeset), ('links', Edgeset), ('parents', Edgeset), ('labelmap', Labelmap)) Graph = Graph.create() # Atomic action. An Action is comprised of a set of these.
def not_contains(self, p: Point2D): i = Const("i", IntSort()) return (ForAll([i], Implies(And(0 <= i, i < self.num_segs), Not(self.segment(i).contains(p)))))
def to_z3_sort(self, ctx: Context) -> SortRef: if self == Kind.INT: return IntSort(ctx) if self == Kind.STR: return StringSort(ctx) raise RuntimeError(self)