def _cell_insertion_custom( self, x: int, y: int, button: int, _modifiers: int ) -> None: """Add a custom obstruction or requirement to a single cell. Args: x (int): The x coordinate of the mouse click. y (int): The y coordinate of the mouse click. button (int): The mouse button clicked. _modifiers (int): If combinded with modifiers (e.g. ctrl). Unused. """ tplot = self._current() if button == pyglet.window.mouse.LEFT: self._add_tiling( tplot.tiling.add_single_cell_requirement( Perm.to_standard(self._custom_data), tplot.get_cell(Point(x, y)), ) ) elif button == pyglet.window.mouse.RIGHT: self._add_tiling( tplot.tiling.add_single_cell_obstruction( Perm.to_standard(self._custom_data), tplot.get_cell(Point(x, y)), ) )
def build_pack(args: argparse.Namespace) -> TileScopePack: if args.strategy_pack not in BASE_PACK: parser.error( "Invalid strategy pack. Use 'tilescope list' to see available packs. " "Perhaps you got the order wrong? A valid command should be of " "the form 'tilescope spec {basis} {pack}'.") pack_builder: PackBuilder = BASE_PACK[args.strategy_pack] kwargs = dict() if args.length is not None: valid_kwarg_or_error(pack_builder, "length", args.strategy_pack) kwargs["length"] = args.length if args.strategy_pack == "regular_insertion_encoding": basis = [Perm.to_standard(p) for p in args.basis.split("_")] if is_insertion_encodable_maximum(basis): kwargs["direction"] = DIR_SOUTH elif is_insertion_encodable_rightmost(basis): kwargs["direction"] = DIR_WEST else: parser.error( "The basis does not have regular insertion encoding, " "so try another pack. Note: the extra args of the 'tilescope' " "command don't work with this pack, use instead the packs " "insertion_row_and_col_placements which expands in a similar " "fashion, but has more powerful strategies.") pack = pack_builder(**kwargs) if args.fusion: pack = pack.make_fusion() if args.database: pack = pack.make_database() if args.symmetries: pack = pack.add_all_symmetry() if args.elementary: pack = pack.make_elementary() return pack
def get_subperm_left_col(self, col: int) -> "GriddedPerm": """Returns the gridded subpermutation of points left of column col.""" return self.__class__( Perm.to_standard( self.patt[i] for i in range(len(self)) if self.pos[i][0] < col ), (self.pos[i] for i in range(len(self)) if self.pos[i][0] < col), )
def get_gridded_perm_in_cells(self, cells: Iterable[Cell]) -> "GriddedPerm": """Returns the subgridded permutation of points in cells.""" return self.__class__( Perm.to_standard( self.patt[i] for i in range(len(self)) if self.pos[i] in cells ), (self.pos[i] for i in range(len(self)) if self.pos[i] in cells), )
def all_subperms(self, proper: bool = True) -> Iterator["GriddedPerm"]: """Yields all gridded subpermutations.""" for r in range(len(self) if proper else len(self) + 1): for subidx in combinations(range(len(self)), r): yield self.__class__( Perm.to_standard(self._patt[i] for i in subidx), (self._pos[i] for i in subidx), )
def get_gridded_perm_in_cells(self, cells: Iterable[Cell]) -> "GriddedPerm": """Returns the subgridded permutation of points in cells.""" cells = set(cells) return type(self)( Perm.to_standard(val for val, pos in self if pos in cells), (pos for pos in self.pos if pos in cells), )
def remove_cells(self, cells: Iterable[Cell]) -> "GriddedPerm": """Remove any points in the cell given and return a new gridded permutation.""" remaining = [i for i in range(len(self)) if self._pos[i] not in cells] return self.__class__( Perm.to_standard(self._patt[i] for i in remaining), (self._pos[i] for i in remaining), )
def remove_cells(self, cells: Iterable[Cell]) -> "GriddedPerm": """Remove any points in the cell given and return a new gridded permutation.""" cells = set(cells) remaining = [i for i, (_, pos) in enumerate(self) if pos not in cells] return type(self)( Perm.to_standard(self._patt[i] for i in remaining), (self._pos[i] for i in remaining), )
def apply_perm_map_to_cell(self, perm_mapping: Callable[[Perm], Perm], cell: Cell) -> "GriddedPerm": """Apply a permutation map to the subperm within a cell.""" subperm = [val for val, pos in self if pos == cell] st = Perm.to_standard(subperm) back_map = dict(zip(st, subperm)) new_subperm = (back_map[val] for val in perm_mapping(st)) return type(self)((next(new_subperm) if pos == cell else val for val, pos in self), self.pos)
def read_stats_json(file, n): f = open(file, 'r') data = json.load(f) f.close() stats = [] try: for perm, value in data['Data'].items(): perm = Perm.to_standard(eval(perm)) if len(perm) <= n: stats.append((perm, value)) except KeyError: for perm, value in data['StatisticData'].items(): perm = Perm.to_standard(eval(perm)) if len(perm) <= n: stats.append((perm, value)) stats.sort() return stats
def get_gridded_perm_at_indices(self, indices: Iterable[int]) -> "GriddedPerm": """ Returns the subgridded perm that contains only the point at the given indices. Indices must be sorted. """ gen1, gen2 = tee(indices, 2) return type(self)( Perm.to_standard(self.patt[i] for i in gen1), (self.pos[i] for i in gen2), )
def get_gridded_perm_at_indices(self, indices: Iterable[int]) -> "GriddedPerm": """ Returns the subgridded perm that contains only the point at the given indices. Indices must be sorted. """ return self.__class__( Perm.to_standard(self.patt[i] for i in indices), (self.pos[i] for i in indices), )
def initial_gp(self, *gps: GriddedPerm) -> GriddedPerm: """ Return the gridded perm that initialises with the first argument, then adds as many points as possible from the left to right, such that the cells don't share a row or column. In this instance, to contain both you must contain the respective subgridded perm in the independent cell. """ # We work with the upward closure. gps = self.get_upward_closure(gps) # Initialise with the first gridded permutation in gps. res = GriddedPerm(gps[0].patt, gps[0].pos) active_cols = set(i for i, j in res.pos) active_rows = set(j for i, j in res.pos) for gp in gps[1:]: # for each subsequent gridded perm in gps, find all the cells # not on the same row or column indep_cells = [(i, j) for i, j in gp.pos if i not in active_cols and j not in active_rows] if indep_cells: # take the subgp using the independent cells. # we will now glue together the result, and the new independent # cell in the unique way that this can be done subgp = gp.get_gridded_perm_in_cells(indep_cells) # update future active rows and cols active_cols.update(i for i, j in subgp.pos) active_rows.update(j for i, j in subgp.pos) # temp will be sorted, and standardised to create the unique # minimal gridded permutation containing both res and subgp. # We want cells at lower index front - if they are in the same # column, then they come from the same gridded permutation, so # we sort second by the index of its original gridded perm. # We will standardise based on values. Those in lower cells # will have smaller value, and if they are in the same row then # they come from the same gridded permutation so the second # entry is the value from its original gridded perm. temp = [ ((cell[0], idx), (cell[1], val)) for (idx, val), cell in zip(enumerate(res.patt), res.pos) ] + [((cell[0], idx), (cell[1], val)) for (idx, val), cell in zip(enumerate(subgp.patt), subgp.pos)] temp.sort() # update the res new_pos = [(idx[0], val[0]) for idx, val in temp] new_patt = Perm.to_standard(val for _, val in temp) res = GriddedPerm(new_patt, new_pos) return res
def insert_req(tiling): """Inserts a req into the cell given by the user.""" if len(tiling.active_cells) == len(tiling.positive_cells): print("All cells already positive. Try some other strategy.") return None if len(tiling.active_cells) == 1: cell = (0, 0) else: print("Which cell do you want to insert into?") cell = get_coordinate(tiling) print("Inserting req into cell {}.".format(cell)) r = input("Type the req you want to insert: ") patt = Perm.to_standard(r) empty = tiling.add_single_cell_obstruction(patt, cell) non_empty = tiling.add_single_cell_requirement(patt, cell) return [empty, non_empty]
def backward_map( self, comb_class: Tiling, objs: Tuple[Optional[GriddedPerm], ...], children: Optional[Tuple[Tiling, ...]] = None, ) -> Iterator[GriddedPerm]: if children is None: children = self.decomposition_function(comb_class) gps_to_combine = tuple( tiling.backward_map.map_gp(cast(GriddedPerm, gp)) for gp, tiling in zip(objs, children)) temp = [((cell[0], idx), (cell[1], val)) for gp in gps_to_combine for (idx, val), cell in zip(enumerate(gp.patt), gp.pos)] temp.sort() new_pos = [(idx[0], val[0]) for idx, val in temp] new_patt = Perm.to_standard(val for _, val in temp) yield GriddedPerm(new_patt, new_pos)
def row_complement(self, row: int) -> "GriddedPerm": """Replace the part of the gridded perm that belongs to a row with its complement.""" indices, vals = set(), [] for i, (val, (_, y)) in enumerate(self): if y == row: indices.add(i) vals.append(val) st = Perm.to_standard(vals) unstandardized = dict(zip(st, vals)) in_row = (unstandardized[val] for val in st.complement()) return type(self)( Perm( next(in_row) if i in indices else val for i, val in enumerate(self.patt)), self.pos, )
def __init__(self, start_class, strategy_pack, # symmetry=False, forward_equivalence=False, logger_kwargs={'processname': 'runner'}, **kwargs): """Initialise TileScope.""" if isinstance(start_class, str): basis = Basis([Perm.to_standard([int(c) for c in p]) for p in start_class.split('_')]) elif isinstance(start_class, list): basis = Basis(start_class) elif isinstance(start_class, Tiling): start_tiling = start_class if start_class.dimensions == (1, 1): basis = Basis([o.patt for o in start_class.obstructions]) else: basis = [] if not isinstance(start_class, Tiling): start_tiling = Tiling( obstructions=[Obstruction.single_cell(patt, (0, 0)) for patt in basis]) if strategy_pack.symmetries==True: symmetries = [Tiling.inverse, Tiling.reverse, Tiling.complement, Tiling.antidiagonal, Tiling.rotate90, Tiling.rotate180, Tiling.rotate270] # symmetries = [sym for sym in symmetries # if sym(start_tiling) == start_tiling] strategy_pack.symmetries = symmetries else: symmetries = [] function_kwargs = {"basis": basis} function_kwargs.update(kwargs.get('kwargs', dict())) CombinatorialSpecificationSearcher.__init__( self, start_tiling, strategy_pack, symmetry=symmetries, forward_equivalence=forward_equivalence, function_kwargs=function_kwargs, logger_kwargs=logger_kwargs, **kwargs)
def permute_rows(self, perm: Iterable[int]) -> "GriddedPerm": """Given an initial state of rows 12...n, permute them using the provided permutation. """ if not isinstance(perm, Perm): perm = Perm(perm) assert len(perm) > max(x for x, y in self.pos) back_map = dict(zip(perm, range(len(perm)))) positions = [(x, back_map[y]) for x, y in self.pos] occ: List[List[int]] = [[] for _ in range(len(perm))] for val, (_, y) in zip(self.patt, positions): occ[y].append(val) offset, val_map = 0, {} for lis in occ: for a, b in zip(lis, Perm.to_standard(lis)): val_map[a] = b + offset offset += len(lis) return type(self)((val_map[val] for val in self.patt), positions)
def __init__(self, start_class: Union[str, Iterable[Perm], Tiling], strategy_pack: TileScopePack, logger_kwargs: Optional[dict] = None, **kwargs) -> None: """Initialise TileScope.""" if isinstance(start_class, str): basis = Basis([ Perm.to_standard([int(c) for c in p]) for p in start_class.split("_") ]) elif isinstance(start_class, Tiling): start_tiling = start_class if start_class.dimensions == (1, 1): basis = Basis([o.patt for o in start_class.obstructions]) elif isinstance(start_class, collections.abc.Iterable): basis = Basis(start_class) assert all(isinstance(p, Perm) for p in basis), "Basis must contains Perm only" else: raise ValueError( "start class must be a string, an iterable of Perm or a tiling" ) if not isinstance(start_class, Tiling): start_tiling = Tiling(obstructions=[ GriddedPerm.single_cell(patt, (0, 0)) for patt in basis ]) if start_tiling.dimensions == (1, 1): procname = kwargs.get("logger_kwargs", {"processname": "runner"}) logger.debug("Fixing basis in OneByOneVerificationStrategy", extra=procname) strategy_pack = strategy_pack.fix_one_by_one(basis) super().__init__( start_tiling, strategy_pack, logger_kwargs=logger_kwargs, **kwargs, )
def verify_letters_and_perms(config, **kwargs): if isinstance(config, Letter): return VerificationRule("Its a letter.") elif config.last_letter is None and len(config.config.slots) == 0: return VerificationRule("Its a permutation.") pack = StrategyPack(initial_strats=[remove_letter, equivalent], inferral_strats=[], expansion_strats=[[expansion]], ver_strats=[verify_letters_and_perms], name=("Finding regular insertion encodings")) if __name__ == "__main__": from permuta.permutils import is_insertion_encodable_maximum basis = input("Enter comma separated permutations: ") basis = [Perm.to_standard(p) for p in basis.split(',')] if is_insertion_encodable_maximum(basis): start_class = Suffixes(Configuration(Perm(), (0, )), basis) print(Configuration(Perm((0, )), (0, ))) searcher = CombinatorialSpecificationSearcher(start_class, pack, debug=True) tree = searcher.auto_search(status_update=10) tree.get_min_poly(solve=True) else: print("Not insertion encodable")
def remove_point(self, index: int) -> "GriddedPerm": """Remove the point at index from the gridded permutation.""" patt = Perm.to_standard(self.patt[:index] + self.patt[index + 1 :]) pos = self.pos[:index] + self.pos[index + 1 :] return self.__class__(patt, pos)
from tilings import Tiling, Requirement from permuta import Perm from permuta.descriptors import Basis from tilescopethree import TileScopeTHREE from tilescopethree.strategy_packs import point_placements from tilescopethree.strategies.equivalence_strategies.point_placements import place_point_of_requirement from tilescopethree.strategies.batch_strategies.cell_insertion import all_cell_insertions from tilescopethree.strategies.equivalence_strategies.fusion_with_interleaving import fusable, fuse_tiling basis = input("Insert basis (in the form 123_132): ") start_tiling = Tiling.from_string(basis) basis = Basis([Perm.to_standard(p) for p in basis.split('_')]) tilescope = TileScopeTHREE(start_tiling, point_placements) options = ("1: insert a point\n" "2: place a point\n" "3: factors\n" "4: fuse\n" "5: insert requirement\n" "-1: undo last move\n" "q: quit") curr_task = [start_tiling] old_tasks = [] def infer(tiling): """Repeatedly apply inferral strategies until no change."""
def parse_formal_step(formal_step): """Parse the formal step to get information about the strategy applied, plus any keywords needed. This will return a partial function that can be applied to give the same combinatorial rule. It will return None if it is a verification strategy.""" def unpacking_generator(start_tiling, strategy, **kwargs): return next(strategy(start_tiling, **kwargs)) def apply_post_map(start_tiling, strategy, **kwargs): # print(start_tiling) # print(strategy) # print(kwargs) end_tilings, forward_maps = strategy(start_tiling, **kwargs) # for t in end_tilings: # print(t) # print(t.forward_map) # for m in forward_maps: # print(m) return end_tilings, mapping_after_initialise(start_tiling, end_tilings, forward_maps) if "Reverse of:" in formal_step: if "reverse" in formal_step: return partial(Tiling.reverse, regions=True) raise ValueError("Can only handle forward equivalence rules!") if "Placing point" in formal_step: _, ri, i, DIR, _ = formal_step.split("|") return partial(place_point_of_requirement, req_index=int(ri), point_index=int(i), force_dir=int(DIR), regions=True) elif "Insert " in formal_step: _, c1, c2, patt, _ = formal_step.split("|") patt = Perm.from_string(patt) cell = (int(c1), int(c2)) return partial(apply_post_map, strategy=cell_insertion, patt=patt, cell=cell, regions=True) elif "factors of the tiling." in formal_step: return partial(Tiling.find_factors, regions=True) elif "Separated rows" in formal_step: return partial(row_and_column_separation, regions=True) elif "Fuse rows" in formal_step: _, row_index, _ = formal_step.split("|") if "fancily" in formal_step: fusion = fancy_fuse_tiling else: fusion = fuse_tiling return partial(fusion, row_index=int(row_index), row=True, regions=True) elif "Fuse columns" in formal_step: _, col_index, _ = formal_step.split("|") if "fancily" in formal_step: fusion = fancy_fuse_tiling else: fusion = fuse_tiling return partial(fusion, row_index=int(col_index), row=False, regions=True) elif "Either row " in formal_step: row = int(formal_step.split(' ')[2]) return partial(apply_post_map, strategy=row_insertion_helper, row=row, row_cells=None, regions=True) elif "Either col " in formal_step: col = int(formal_step.split(' ')[2]) return partial(apply_post_map, strategy=col_insertion_helper, col=col, col_cells=None, regions=True) elif "Placing row" in formal_step or "Placing col" in formal_step: row = "row" in formal_step _, idx, direction, positive, _ = formal_step.split("|") return partial(apply_post_map, strategy=partial(unpacking_generator, strategy=row_placements, row=row, positive=bool(int(positive)), index=int(idx), direction=int(direction), regions=True)) elif "The tiling is a subset of the class" in formal_step: return None elif "Inserting " in formal_step: front, _, _ = formal_step.split('~') _, patt, pos, _ = formal_step.split("|") position = [] for cell in pos.split("/"): x, y = cell.split(",") position.append((int(x), int(y))) gp = GriddedPerm(Perm.to_standard(patt), position) return partial(apply_post_map, strategy=gp_insertion, gp=gp, regions=True) elif "Greedily placing points" in formal_step: return partial(apply_post_map, strategy=greedy_griddings, formal_step=formal_step) else: raise NotImplementedError("Not tracking regions for: " + formal_step)
def get_subperm_left_col(self, col: int) -> "GriddedPerm": """Returns the gridded subpermutation of points left of column col.""" gen1, gen2 = tee((i for i, _ in self.get_points_left_col(col)), 2) return type(self)(Perm.to_standard(self.patt[i] for i in gen1), (self.pos[i] for i in gen2))
def __init__(self, start_tiling, end_tilings, start_label, end_labels, formal_step): """Parse the formal step to be able to trace regions of rules.""" if "Reverse of:" in formal_step: new_start_tiling = end_tilings[0] end_tilings = [start_tiling] start_tiling = new_start_tiling self.start_label = start_label if "Placing point" in formal_step: _, ri, i, DIR, _ = formal_step.split("|") rule = place_point_of_requirement(start_tiling, int(ri), int(i), int(DIR), regions=True) self.constructor = "disjoint" elif "Insert " in formal_step: _, c1, c2, patt, _ = formal_step.split("|") cell = (int(c1), int(c2)) patt = Perm.to_standard(patt) avoids = start_tiling.add_single_cell_obstruction(patt, cell) contains = start_tiling.add_single_cell_requirement(patt, cell) # print(avoids) # print(avoids.forward_map) # print(contains) # print(contains.forward_map) rule = ([avoids, contains], [ { c: set([avoids.forward_map[c]]) for c in start_tiling.active_cells if (c in avoids.forward_map and avoids.forward_map[c] in avoids.active_cells) }, { c: set([contains.forward_map[c]]) for c in start_tiling.active_cells if (c in contains.forward_map and contains.forward_map[c] in contains.active_cells) } ]) self.constructor = "disjoint" elif "factors of the tiling." in formal_step: rule = start_tiling.find_factors(regions=True) self.constructor = "cartesian" elif "Separated rows" in formal_step: rule = row_and_column_separation(start_tiling, regions=True) self.constructor = "disjoint" elif "Fuse rows" in formal_step: _, row_index, _ = formal_step.split("|") rule = fuse_tiling(start_tiling, int(row_index), True, regions=True) self.constructor = "fusion" elif "Fuse columns" in formal_step: _, col_index, _ = formal_step.split("|") rule = fuse_tiling(start_tiling, int(col_index), False, regions=True) self.constructor = "fusion" elif "The tiling is a subset of the class" in formal_step: rule = ([], []) self.constructor = "verified" elif "Placing row" in formal_step or "Placing col" in formal_step: row = "row" in formal_step direction = int(formal_step.split(".")[0][-1]) positive = bool(int(formal_step.split("|")[1])) rule = list( row_placements(start_tiling, row=row, positive=positive, regions=True, direction=direction))[0] # print(rule) self.constructor = "disjoint" else: raise NotImplementedError("Not tracking regions for: " + formal_step) self.start_tiling = start_tiling # if rule is None: # print(start_tiling) # print(formal_step) self.end_tilings, self.regions = rule empty = [t.is_empty() for t in self.end_tilings] self.end_tilings = [ t for i, t in enumerate(self.end_tilings) if not empty[i] ] self.regions = [r for i, r in enumerate(self.regions) if not empty[i]] self.formal_step = formal_step if set(self.end_tilings) != set(end_tilings): raise ValueError("Reapplying strategy failed.") assert set(self.end_tilings) == set( end_tilings), "strategy gave different output second time round" labels = {t: l for t, l in zip(end_tilings, end_labels)} self.end_labels = [labels[t] for t in self.end_tilings]
def get_subrules(self): # Subrules are MeshTilings of sizes ranging from 1 x 1 to 3 x 3 # (adjustable with self.MAX_COLUMN_DIMENSION and self.MAX_ROW_DIMENSION # variables). Each cell contains a mix of requirements (Perms) and # obstructions (MeshPatts) where the obstructions are sub mesh patterns # of any of the obstructions in the root object. logger.info("About to generate subrules with up to {} active cells " "and dimensions up to {}x{}".format( self.MAX_ACTIVE_CELLS, self.MAX_COLUMN_DIMENSION, self.MAX_ROW_DIMENSION)) perms, mesh_patts = set(), set() for obstructions_list in chain(self.get_obstructions_lists(), self.get_requirements_lists()): for obstruction in obstructions_list: n = len(obstruction) if isinstance(obstruction, Perm): for l in range(n): perms.update( Perm.to_standard((obstruction[i] for i in indices)) for indices in combinations(range(n), l + 1)) elif isinstance(obstruction, MeshPatt): for l in range(n): for indices in combinations(range(n), l + 1): mesh_patt = obstruction.sub_mesh_pattern(indices) if len(mesh_patt.shading) > 0: mesh_patts.add(mesh_patt) else: # A mesh patt without shading is just a perm perms.add(mesh_patt.pattern) else: raise ValueError( "[ERROR] obstruction '{}' is neither a MeshPatt " "or Perm!".format(obstruction)) origin_cell = self.tiling[0] cell_choices = {self.point_cell, self.anything_cell} cell_choices.add( origin_cell if origin_cell.is_avoiding() else origin_cell.flip()) for patt in Utils.clean_patts(perms, mesh_patts): av_cell = Cell(frozenset({patt}), frozenset()) if not av_cell.is_empty(): cell_choices.add(av_cell) logger.info("{} cell choices: {}".format(len(cell_choices), cell_choices)) subrules = 1 yield MeshTiling() # always include the empty rule for (dim_col, dim_row) in product( range(1, self.columns + self.MAX_COLUMN_DIMENSION), range(1, self.rows + self.MAX_ROW_DIMENSION)): nr_of_cells = dim_col * dim_row for how_many_active_cells in range(min(dim_col, dim_row), self.MAX_ACTIVE_CELLS + 1): for active_cells in product(cell_choices, repeat=how_many_active_cells): for combination in combinations(range(nr_of_cells), how_many_active_cells): cells = {} for i, cell_index in enumerate(combination): c = cell_index % dim_col r = cell_index // dim_col cells[(c, r)] = active_cells[i] subrules += 1 yield MeshTiling(cells) logger.info("Generated in total {} subrules ".format(subrules))