def components(tiling): """Return the component of a tiling. Two cells are in the same component if they are in the same row or column.""" n, m = tiling.dimensions def cell_to_int(cell): return cell[0] * m + cell[1] def int_to_cell(i): return (i // m, i % m) cells = list(tiling.active_cells) uf = UnionFind(n * m) for i in range(len(cells)): for j in range(i + 1, len(cells)): c1, c2 = cells[i], cells[j] if c1[0] == c2[0] or c1[1] == c2[1]: uf.unite(cell_to_int(c1), cell_to_int(c2)) # Collect the connected components of the cells all_components = {} for cell in cells: i = uf.find(cell_to_int(cell)) if i in all_components: all_components[i].append(cell) else: all_components[i] = [cell] component_cells = list(set(cells) for cells in all_components.values()) return component_cells
def __init__(self, tiling: "Tiling") -> None: self._tiling = tiling self._active_cells = tiling.active_cells nrow = tiling.dimensions[1] ncol = tiling.dimensions[0] self._cell_unionfind = UnionFind(nrow * ncol) self._components: Optional[Tuple[Set[Cell], ...]] = None self._factors_obs_and_reqs: Optional[List[Tuple[Tuple[ GriddedPerm, ...], Tuple[ReqList, ...], Tuple[TrackingAssumption, ...], ]]] = None
def factors(self) -> List["GriddedPerm"]: """Return a list containing the factors of a gridded permutation. A factor is a sub gridded permutation that is isolated on its own rows and columns.""" uf = UnionFind(len(self.pos)) for j, (_, (x_r, y_r)) in enumerate(self): for i, (_, (x_l, y_l)) in enumerate(islice(self, j)): if x_l == x_r or y_l == y_r: uf.unite(i, j) # Collect the connected factors of the cells all_factors: Dict[int, List[Cell]] = {} for i, cell in enumerate(self.pos): x = uf.find(i) if x in all_factors: all_factors[x].append(cell) else: all_factors[x] = [cell] factor_cells = list(set(cells) for cells in all_factors.values()) return [self.get_gridded_perm_in_cells(comp) for comp in factor_cells]
def factors(self) -> List["GriddedPerm"]: """Return a list containing the factors of a gridded permutation. A factor is a sub gridded permutation that is isolated on its own rows and columns.""" uf = UnionFind(len(self.pos)) for i in range(len(self.pos)): for j in range(i + 1, len(self.pos)): c1, c2 = self.pos[i], self.pos[j] if c1[0] == c2[0] or c1[1] == c2[1]: uf.unite(i, j) # Collect the connected factors of the cells all_factors: Dict[Cell, List[Cell]] = {} for i, cell in enumerate(self.pos): x = uf.find(i) if x in all_factors: all_factors[x].append(cell) else: all_factors[x] = [cell] factor_cells = list(set(cells) for cells in all_factors.values()) return [self.get_gridded_perm_in_cells(comp) for comp in factor_cells]
def is_boundary_anchored(patt): inv = patt.pattern.inverse() right, top, left, bottom = patt.has_anchored_point() in_bound = set() if not right and not left and not top and not bottom: return False if right: in_bound.add(len(patt) - 1) if left: in_bound.add(0) if top: in_bound.add(inv[len(patt) - 1]) if bottom: in_bound.add(inv[0]) uf = UnionFind(len(patt)) for i in range(1, len(patt)): if all((i, j) in patt.shading for j in range(len(patt) + 1)): uf.unite(i - 1, i) if all((j, i) in patt.shading for j in range(len(patt) + 1)): uf.unite(inv[i - 1], inv[i]) # print(uf.leaders) return uf.leaders == set(uf.find(b) for b in in_bound)
def factor(tiling, **kwargs): """ The factor strategy that decomposes a tiling into its connected factors. The factors are the connected components of the graph of the tiling, where vertices are the cells. Two vertices are connected if there exists a obstruction or requirement occupying both cells. Two cells are also connected if they share the same row or column unless the interleaving or point_interleaving keyword arguments are set to True. When point interleavings are allowed, two cells in the same row or column are not connected. When general interleavings are allowed, two cells in the same row or column are not connected. """ interleaving = kwargs.get("interleaving", False) point_interleaving = kwargs.get("point_interleaving", False) n, m = tiling.dimensions def cell_to_int(cell): return cell[0] * m + cell[1] def int_to_cell(i): return (i // m, i % m) cells = list(tiling.active_cells) uf = UnionFind(n * m) # Unite by obstructions for ob in tiling.obstructions: for i in range(len(ob.pos)): for j in range(i+1, len(ob.pos)): uf.unite(cell_to_int(ob.pos[i]), cell_to_int(ob.pos[j])) # Unite by requirements for req_list in tiling.requirements: req_cells = list(union_reduce(req.pos for req in req_list)) for i in range(len(req_cells)): for j in range(i + 1, len(req_cells)): uf.unite(cell_to_int(req_cells[i]), cell_to_int(req_cells[j])) # If interleaving not allowed, unite by row/col if not interleaving: for i in range(len(cells)): for j in range(i+1, len(cells)): c1, c2 = cells[i], cells[j] if (point_interleaving and (c1 in tiling.point_cells or c2 in tiling.point_cells)): continue if c1[0] == c2[0] or c1[1] == c2[1]: uf.unite(cell_to_int(c1), cell_to_int(c2)) # Collect the connected components of the cells all_components = {} for cell in cells: i = uf.find(cell_to_int(cell)) if i in all_components: all_components[i].append(cell) else: all_components[i] = [cell] component_cells = list(set(cells) for cells in all_components.values()) # If the tiling is a single connected component if len(component_cells) <= 1: return # Collect the factors of the tiling factors = [] strategy = [] # the vanilla factors for cell_component in component_cells: obstructions = [ob for ob in tiling.obstructions if ob.pos[0] in cell_component] requirements = [req for req in tiling.requirements if req[0].pos[0] in cell_component] if obstructions or requirements: factors.append((obstructions, requirements)) strategy.append(Tiling(obstructions=obstructions, requirements=requirements, minimize=False)) if kwargs.get("workable", True): work = [True for _ in strategy] else: work = [False for _ in strategy] yield Rule("The factors of the tiling.", strategy, inferable=[False for _ in strategy], workable=work, possibly_empty=[False for _ in strategy], ignore_parent=kwargs.get("workable", True), constructor='cartesian') if kwargs.get("unions", False): for partition in partition_list(factors): strategy = [] for part in partition: obstructions, requirements = zip(*part) strategy.append(Tiling(obstructions=chain(*obstructions), requirements=chain(*requirements), minimize=False)) yield Rule("The union of factors of the tiling", strategy, possibly_empty=[False for _ in strategy], inferable=[False for _ in strategy], workable=[False for _ in strategy], constructor='cartesian')