Beispiel #1
0
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
Beispiel #2
0
 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]
Beispiel #3
0
 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]
Beispiel #4
0
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)
Beispiel #5
0
        lst = map(int,val.split())
        surprising.append(lst)
    else:
        assert False

f.close()


coinc = []
pat = '\\pattern{scale=0.75}{%d}{%s}' % (len(perm), ','.join([ '%d/%s' % (i+1,v) for i,v in enumerate(perm) ]))

for lst in surprising:
    co = []
    for p in sorted(lst):
        # XXX: make this faster, if needed
        here = [ i for i in range(2**((n+1)**2)) if uf.find(i) == uf.find(p) ]
        best = -1
        for x in here:
            if best == -1 or hamming(best) < hamming(x):
                best = x
        co.append(best)

    s = '\\begin{center}\n'
    s += 'Coincidence %d\n' % (len(coinc)+1)

    print co

    for x in co:
        s += pat + '{' + ','.join( '%d/%d' % (i//(n+1),i%(n+1)) for i in range((n+1)**2) if (x & (1<<i)) != 0 ) + '}\n'

    s += '\\end{center}\n'
Beispiel #6
0
class Factor:
    """
    Algorithm to compute the factorisation of a tiling.

    Two active cells are in the same factor if they are in the same row
    or column, or they share an obstruction or a requirement.

    If using tracking assumptions, then two cells will also be in the same
    factor if they are covered by the same assumption.
    """

    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 _cell_to_int(self, cell: Cell) -> int:
        nrow = self._tiling.dimensions[1]
        return cell[0] * nrow + cell[1]

    def _int_to_cell(self, i: int) -> Cell:
        nrow = self._tiling.dimensions[1]
        return (i // nrow, i % nrow)

    def _get_cell_representative(self, cell: Cell) -> Cell:
        """
        Return the representative of a cell in the union find.
        """
        i = self._cell_to_int(cell)
        return self._cell_unionfind.find(i)  # type: ignore

    def _unite_cells(self, cells: Iterable[Cell]) -> None:
        """
        Put all the cells of `cells` in the same component of the UnionFind.
        """
        cell_iterator = iter(cells)
        c1 = next(cell_iterator)
        c1_int = self._cell_to_int(c1)
        for c2 in cell_iterator:
            c2_int = self._cell_to_int(c2)
            self._cell_unionfind.unite(c1_int, c2_int)

    def _unite_assumptions(self) -> None:
        """
        For each TrackingAssumption unite all the positions of the gridded perms.
        """
        for assumption in self._tiling.assumptions:
            if isinstance(assumption, ComponentAssumption):
                self._unite_cells(chain.from_iterable(gp.pos for gp in assumption.gps))
            else:
                for gp in assumption.gps:
                    self._unite_cells(gp.pos)

    def _unite_obstructions(self) -> None:
        """
        For each obstruction unite all the position of the obstruction.
        """
        for ob in self._tiling.obstructions:
            self._unite_cells(ob.pos)

    def _unite_requirements(self) -> None:
        """
        For each requirement unite all the cell in all the requirements of the
        list.
        """
        for req_list in self._tiling.requirements:
            req_cells = chain.from_iterable(req.pos for req in req_list)
            self._unite_cells(req_cells)

    @staticmethod
    def _same_row_or_col(cell1: Cell, cell2: Cell) -> bool:
        """
        Test if `cell1` and `cell2` or in the same row or columns
        """
        return cell1[0] == cell2[0] or cell1[1] == cell2[1]

    def _unite_rows_and_cols(self) -> None:
        """
        Unite all the active cell that are on the same row or column.
        """
        cell_pair_to_unite = (
            c
            for c in combinations(self._active_cells, r=2)
            if self._same_row_or_col(c[0], c[1])
        )
        for c1, c2 in cell_pair_to_unite:
            self._unite_cells((c1, c2))

    def _unite_all(self) -> None:
        """
        Unite all the cells that share an obstruction, a requirement list,
        a row or a column.
        """
        self._unite_obstructions()
        self._unite_requirements()
        self._unite_assumptions()
        self._unite_rows_and_cols()

    def get_components(self) -> Tuple[Set[Cell], ...]:
        """
        Returns the tuple of all the components. Each component is set of
        cells.
        """
        if self._components is not None:
            return self._components
        self._unite_all()
        all_components: Dict[Cell, Set[Cell]] = defaultdict(set)
        for cell in self._active_cells:
            rep = self._get_cell_representative(cell)
            all_components[rep].add(cell)
        self._components = tuple(all_components.values())
        return self._components

    def _get_factors_obs_and_reqs(
        self,
    ) -> List[
        Tuple[
            Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple[TrackingAssumption, ...]
        ],
    ]:
        """
        Returns a list of all the irreducible factors of the tiling.
        Each factor is a tuple (obstructions, requirements)
        """
        if self._factors_obs_and_reqs is not None:
            return self._factors_obs_and_reqs
        factors = []
        for component in self.get_components():
            obstructions = tuple(
                ob for ob in self._tiling.obstructions if ob.pos[0] in component
            )
            requirements = tuple(
                req for req in self._tiling.requirements if req[0].pos[0] in component
            )
            # TODO: consider skew/sum assumptions
            assumptions = tuple(
                ass.__class__(gp for gp in ass.gps if gp.pos[0] in component)
                for ass in self._tiling.assumptions
            )
            factors.append(
                (
                    obstructions,
                    requirements,
                    tuple(set(ass for ass in assumptions if ass.gps)),
                )
            )
        self._factors_obs_and_reqs = factors
        return self._factors_obs_and_reqs

    def factorable(self) -> bool:
        """
        Returns `True` if the tiling has more than one factor.
        """
        return len(self.get_components()) > 1

    def factors(self) -> Tuple["Tiling", ...]:
        """
        Returns all the irreducible factors of the tiling.
        """
        return tuple(
            self._tiling.__class__(
                obstructions=f[0], requirements=f[1], assumptions=f[2], simplify=False
            )
            for f in self._get_factors_obs_and_reqs()
        )

    def reducible_factorisations(self) -> Iterator[Tuple["Tiling", ...]]:
        """
        Iterator over all reducible factorisation that can be obtained by
        grouping of irreducible factors.

        Each factorisation is a list of tiling.

        For example if T = T1 x T2 x T3 then (T1 x T3) x T2 is a possible
        reducible factorisation.
        """
        min_comp = self._get_factors_obs_and_reqs()
        for partition in partitions_iterator(min_comp):
            factors = []
            for part in partition:
                obstructions, requirements, assumptions = zip(*part)
                factors.append(
                    self._tiling.__class__(
                        obstructions=chain(*obstructions),
                        requirements=chain(*requirements),
                        assumptions=chain(*assumptions),
                        simplify=False,
                    )
                )
            yield tuple(factors)
Beispiel #7
0
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')