示例#1
0
    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)),
                )
            )
示例#2
0
文件: cli.py 项目: odinn13/Tilings
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
示例#3
0
 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),
     )
示例#4
0
 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),
     )
示例#5
0
 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),
             )
示例#6
0
 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),
     )
示例#7
0
 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),
     )
示例#8
0
 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),
     )
示例#9
0
 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)
示例#10
0
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
示例#11
0
 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),
     )
示例#12
0
    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),
        )
示例#13
0
 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
示例#14
0
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]
示例#15
0
 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)
示例#16
0
 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,
     )
示例#17
0
    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)
示例#18
0
 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)
示例#19
0
    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,
        )
示例#20
0
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")
示例#21
0
 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)
示例#22
0
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."""
示例#23
0
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)
示例#24
0
 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]
示例#26
0
    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))