From 26176c10abac83443ff8859e6002072f937e16df Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Wed, 25 Aug 2021 12:18:28 +0000 Subject: [PATCH 01/41] create the general fusion algorithm --- tilings/algorithms/factor.py | 2 +- tilings/algorithms/fusion.py | 9 +- tilings/algorithms/general_fusion.py | 419 +++++++++++++++++++++++++++ tilings/assumptions.py | 306 ++++++++----------- tilings/tiling.py | 27 +- 5 files changed, 557 insertions(+), 206 deletions(-) create mode 100644 tilings/algorithms/general_fusion.py diff --git a/tilings/algorithms/factor.py b/tilings/algorithms/factor.py index 294f2298..fafb635f 100644 --- a/tilings/algorithms/factor.py +++ b/tilings/algorithms/factor.py @@ -4,7 +4,7 @@ from permuta.misc import UnionFind from tilings import GriddedPerm -from tilings.assumptions import ComponentAssumption, TrackingAssumption +from tilings.assumptions import TrackingAssumption from tilings.misc import partitions_iterator if TYPE_CHECKING: diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 61c16d17..3c9b518a 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -5,12 +5,7 @@ from itertools import chain from typing import TYPE_CHECKING, Counter, Iterable, Iterator, List, Optional, Tuple -from tilings.assumptions import ( - ComponentAssumption, - SkewComponentAssumption, - SumComponentAssumption, - TrackingAssumption, -) +from tilings.assumptions import TrackingAssumption from tilings.griddedperm import GriddedPerm if TYPE_CHECKING: @@ -603,7 +598,7 @@ def fusable(self) -> bool: return self._tiling == new_tiling and self._check_isolation_level() - def new_assumption(self) -> ComponentAssumption: + def new_assumption(self) -> TrackingAssumption: """ Return the assumption that needs to be counted in order to enumerate. """ diff --git a/tilings/algorithms/general_fusion.py b/tilings/algorithms/general_fusion.py new file mode 100644 index 00000000..1fbd1bf9 --- /dev/null +++ b/tilings/algorithms/general_fusion.py @@ -0,0 +1,419 @@ +from collections import defaultdict +from itertools import chain, product +from typing import Counter, DefaultDict, Dict, Iterable, Iterator, List, Optional, Tuple + +from tilings.algorithms.fusion import Fusion +from tilings.assumptions import TrackingAssumption +from tilings.griddedperm import Cell, GriddedPerm +from tilings.misc import union_reduce +from tilings.tiling import Tiling + + +class GeneralFusion(Fusion): + def __init__(self, *args, **kwargs): + self._extra_obs: Optional[List[GriddedPerm]] = None + self._extra_reqs: Optional[List[List[GriddedPerm]]] = None + super().__init__(*args, **kwargs) + + def is_crossing(self, gp: GriddedPerm) -> bool: + """ + Return True if the gridded permutation `gp` is + crossing between the fused rows or cols. + """ + return bool( + self._fuse_row + and any(y == self._row_idx for _, y in gp.pos) + and any(y == self._row_idx for _, y in gp.pos) + ) or bool( + not self._fuse_row + and any(x == self._col_idx for x, _ in gp.pos) + and any(x == self._col_idx for x, _ in gp.pos) + ) + + @property + def obstruction_fuse_counter(self) -> Counter[GriddedPerm]: + """ + Counter of multiplicities of fused obstructions. + + Crossing obstructions between first cell and second cell + are ignored. + """ + if self._obstruction_fuse_counter is not None: + return self._obstruction_fuse_counter + obs = (ob for ob in self._tiling.obstructions if not self.is_crossing(ob)) + fuse_counter = self._fuse_counter(obs) + self._obstruction_fuse_counter = fuse_counter + return self._obstruction_fuse_counter + + @property + def requirements_fuse_counters(self) -> List[Counter[GriddedPerm]]: + """ + List of fuse counters for each of the requirements list of the tiling. + """ + if self._requirements_fuse_counters is not None: + return self._requirements_fuse_counters + counters = [ + self._fuse_counter(req_list) + for req_list in self._tiling.requirements + # if not self.is_positive_left_or_right_requirement(req_list) + # TODO: don't check for positive cells + ] + self._requirements_fuse_counters = counters + return self._requirements_fuse_counters + + def obstructions_to_add(self) -> Iterator[GriddedPerm]: + """ + Iterator over all the obstructions obtained by fusing obstructions of + the tiling and then unfusing it in all possible ways. Crossing + obstructions between first cell and second cell are not processed. + """ + return chain.from_iterable( + self.unfuse_gridded_perm(ob) for ob in self.obstruction_fuse_counter + ) + + def requirements_to_add(self) -> Iterator[Iterator[GriddedPerm]]: + for req in self.requirements_fuse_counters: + yield chain.from_iterable(self.unfuse_gridded_perm(gp) for gp in req) + + def _can_fuse_set_of_gridded_perms( + self, fuse_counter: Counter[GriddedPerm] + ) -> bool: + raise NotImplementedError + + def _is_valid_count(self, count: int, gp: GriddedPerm) -> bool: + raise NotImplementedError + + def fusable(self) -> bool: + split_obs = tuple(self.obstructions_to_add()) + split_reqs = tuple(tuple(req) for req in self.requirements_to_add()) + new_tiling = self._tiling.add_obstructions_and_requirements( + split_obs, split_reqs, remove_empty_rows_and_cols=False + ) + self._extra_obs = [ + gp for gp in self._tiling.obstructions if gp not in split_obs + ] + extra_reqs = [ + [gp for gp in split_req if gp in req] + for req, split_req in zip(self._tiling.requirements, split_reqs) + ] + self._extra_reqs = [req for req in extra_reqs if req] + return ( + self._tiling == new_tiling + and not extra_reqs + and all(len(gp) <= 2 for gp in self._extra_obs) + # and len(self._extra_obs) <= 1 + and self._check_isolation_level() + and not any( + ass.is_active_in_cells(self.active_cells()) + and ass.fuse( + self._row_idx if self._fuse_row else self._col_idx, self._fuse_row + ) + != self.new_assumption() + for ass in self._tiling.assumptions + ) + # and self.new_assumption().tiling.dimensions + # in ((2, 1), (1, 2)) # TODO: this is perhaps too restrictive? + ) + + def active_cells(self): + if self._fuse_row: + return self._tiling.cells_in_row(self._row_idx).union( + self._tiling.cells_in_row(self._row_idx + 1) + ) + else: + return self._tiling.cells_in_col(self._col_idx).union( + self._tiling.cells_in_col(self._col_idx + 1) + ) + + def new_assumption(self) -> TrackingAssumption: + """ + Return the assumption that needs to be counted in order to enumerate. + """ + # if not self.fusable(): + # raise ValueError("Tiling is not fusable") + if self._extra_obs is None: + assert self.fusable() + assert self._extra_obs is not None + assert self._extra_reqs is not None + tiling = self._tiling + col_map: Dict[int, int] = dict() + row_map: Dict[int, int] = dict() + for x in range(self._tiling.dimensions[0]): + if not self._fuse_row and x > self._col_idx: + col_map[x] = x - 1 + if not self._fuse_row and x <= self._col_idx: + col_map[x] = x + if self._fuse_row: + col_map[x] = x + for y in range(self._tiling.dimensions[1]): + if self._fuse_row and y > self._row_idx: + row_map[y] = y - 1 + if self._fuse_row and y <= self._row_idx: + row_map[y] = y + if not self._fuse_row: + row_map[y] = y + return TrackingAssumption(tiling, col_map, row_map) + + +def test_ass_map(tiling, original_tiling, verbose=False): + return + if verbose: + print("=" * 10) + print("TESTING ASS MAP") + print(tiling) + print(original_tiling) + ass = tiling.assumptions[0] + original_obs, original_reqs = ass.obstructions_and_requirements() + new_obs = [] + for ob in tiling.obstructions: + if verbose: + print(ob) + for gp in ass.reverse_map(ob): + if gp.avoids(*original_obs): + if verbose: + print(" ", gp) + new_obs.append(gp) + new_reqs = [] + for req in tiling.requirements: + new_req = [] + for r in req: + if verbose: + print(r) + for gp in ass.reverse_map(r): + if any(gp.contains(*gps) for gps in original_reqs): + if verbose: + print(" ", gp) + new_req.append(gp) + new_reqs.append(new_req) + unfused = Tiling( + new_obs + original_obs, + new_reqs + original_reqs, + ) + if verbose: + # print(original_tiling) + print(unfused) + print("=" * 10) + assert unfused == original_tiling.remove_assumptions() + for i in range(6): # these counts should match! + terms = tiling.get_terms(i) + actual = len(list(original_tiling.remove_assumptions().objects_of_size(i))) + computed = sum(k[0] * v for k, v in terms.items()) + assert actual == computed, (i, actual, computed, terms, tiling, original_tiling) + + +if __name__ == "__main__": + + tiling = Tiling.from_dict( + { + "class_module": "tilings.tiling", + "comb_class": "Tiling", + "obstructions": [ + {"patt": [0, 1], "pos": [[0, 0], [1, 0]]}, + {"patt": [0, 1], "pos": [[0, 0], [3, 0]]}, + {"patt": [0, 1], "pos": [[1, 0], [3, 0]]}, + {"patt": [0, 1], "pos": [[2, 0], [3, 0]]}, + {"patt": [0, 2, 1], "pos": [[0, 0], [0, 0], [0, 0]]}, + {"patt": [0, 2, 1], "pos": [[0, 0], [0, 0], [2, 0]]}, + {"patt": [0, 2, 1], "pos": [[0, 0], [2, 0], [2, 0]]}, + {"patt": [0, 2, 1], "pos": [[1, 0], [1, 0], [1, 0]]}, + {"patt": [0, 2, 1], "pos": [[1, 0], [1, 0], [2, 0]]}, + {"patt": [0, 2, 1], "pos": [[1, 0], [2, 0], [2, 0]]}, + {"patt": [0, 2, 1], "pos": [[2, 0], [2, 0], [2, 0]]}, + {"patt": [0, 2, 1], "pos": [[3, 0], [3, 0], [3, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[0, 0], [0, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[0, 0], [2, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[0, 0], [4, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[1, 0], [1, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[1, 0], [2, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[1, 0], [4, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[2, 0], [2, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[2, 0], [4, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[3, 0], [3, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[3, 0], [4, 0], [4, 0], [4, 0]]}, + {"patt": [0, 2, 1, 3], "pos": [[4, 0], [4, 0], [4, 0], [4, 0]]}, + ], + "requirements": [], + "assumptions": [], + } + ) + + gf = GeneralFusion(tiling, col_idx=0, tracked=True) + print(gf.fusable()) + print(gf.fused_tiling()) + test_ass_map(gf.fused_tiling(), tiling) + + fusable = Tiling( + [ + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + ] + ) + + gf = GeneralFusion(tiling=fusable, col_idx=1, tracked=True) + + print(gf.fusable()) + print(gf.fused_tiling()) + + test_ass_map(gf.fused_tiling(), fusable) + + for i in range(5): + terms = gf.fused_tiling().get_terms(i) + print(i, terms) + print("actual:", len(list(gf._tiling.objects_of_size(i)))) + print("computed:", sum(k[0] * v for k, v in terms.items())) + + comp_fusable = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 2), (0, 2))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 2), (0, 0))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 2), (0, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 0), (0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (0, 2))), + GriddedPerm((0, 3, 1, 2), ((0, 0), (0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 3, 1, 2), ((0, 2), (0, 2), (0, 2), (0, 2))), + ), + requirements=(), + assumptions=(), + ) # 3 without assumption + + gf = GeneralFusion(comp_fusable, row_idx=0, tracked=True) + fused_tiling = gf.fused_tiling() # 2 with assumption + + test_ass_map(fused_tiling, comp_fusable) + + ass = fused_tiling.assumptions[0] + print("===== component fusable tiling =====") + print(comp_fusable) + print("==== end ====") + + print("===== the fused tiling =====") + print(fused_tiling) + print("==== end ====") + + for i in range(6): + terms = fused_tiling.get_terms(i) + print(i, terms) + print("actual:", len(list(gf._tiling.objects_of_size(i)))) + print("computed:", sum(k[0] * v for k, v in terms.items())) + + unfused_positive_tiling = comp_fusable.insert_cell((0, 2)) + + positive_fused_tiling = fused_tiling.insert_cell((0, 1)) + + test_ass_map(positive_fused_tiling, unfused_positive_tiling) + + print("===== the positive tiling =====") + print(positive_fused_tiling) # 5 with assumption? + print("==== end ====") + + unfused_placed_tiling = unfused_positive_tiling.place_point_in_cell((0, 2), 0) + + placed_fused_tiling = positive_fused_tiling.place_point_in_cell((0, 1), 0) + + test_ass_map(placed_fused_tiling, unfused_placed_tiling, verbose=False) + + print("===== the placed tiling =====") + print(placed_fused_tiling) # 5.5, i.e. the one in the middle of the eqv path 5 -> 6 + print("==== end ====") + separated_tiling = placed_fused_tiling.row_and_column_separation() + unfused_separated_tiling = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 1), (0, 4))), + GriddedPerm((0, 1), ((0, 1), (2, 2))), + GriddedPerm((0, 1), ((0, 3), (0, 4))), + GriddedPerm((0, 1), ((1, 5), (1, 5))), + GriddedPerm((0, 1), ((2, 0), (2, 2))), + GriddedPerm((1, 0), ((1, 5), (1, 5))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 6), (0, 6))), + GriddedPerm((0, 1, 2), ((0, 3), (0, 6), (0, 6))), + GriddedPerm((0, 1, 2), ((0, 4), (0, 6), (0, 6))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 6), (0, 1))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 6), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 4), (0, 6), (0, 4))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 3), (0, 3), (0, 3), (0, 3))), + GriddedPerm((0, 2, 3, 1), ((0, 4), (0, 4), (0, 4), (0, 4))), + GriddedPerm((0, 2, 3, 1), ((0, 6), (0, 6), (0, 6), (0, 6))), + GriddedPerm((0, 2, 3, 1), ((2, 0), (2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 2, 3, 1), ((2, 2), (2, 2), (2, 2), (2, 2))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 3, 1, 2), ((0, 3), (0, 3), (0, 3), (0, 3))), + GriddedPerm((0, 3, 1, 2), ((0, 4), (0, 4), (0, 4), (0, 4))), + GriddedPerm((0, 3, 1, 2), ((0, 6), (0, 6), (0, 6), (0, 6))), + GriddedPerm((0, 3, 1, 2), ((2, 0), (2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 3, 1, 2), ((2, 2), (2, 2), (2, 2), (2, 2))), + ), + requirements=((GriddedPerm((0,), ((1, 5),)),),), + assumptions=(), + ) # only separating rows, so can't use row_column_separation method + + print(repr(unfused_separated_tiling)) + print(separated_tiling) + test_ass_map(separated_tiling, unfused_separated_tiling, verbose=True) + + # separated_assless_tiling = ( + # placed_fused_tiling.remove_assumptions().row_and_column_separation() + # ) + # print(separated_assless_tiling) + # separated_comp_fusable = Tiling( + # [ + # GriddedPerm((0, 1), ((0, 1), (2, 2))), + # GriddedPerm((0, 1), ((2, 0), (2, 2))), + # GriddedPerm((0, 1), ((0, 1), (0, 3))), + # ], + # remove_empty_rows_and_cols=False, + # ) + # print(separated_comp_fusable) + # separable_map = { + # (0, 1): (0, 1), + # (0, 3): (0, 1), + # (1, 0): (1, 0), + # (2, 2): (2, 0), + # (2, 0): (2, 0), + # } + + # separable_ass = TrackingAssumption(separated_comp_fusable, separable_map) + # unfused_separated_tiling = unfused_placed_tiling.row_and_column_separation() + # separated_tiling = separated_assless_tiling.add_assumption(separable_ass) + print("===== the separated tiling =====") + print(separated_tiling) # 6, i.e. the one in the middle of the eqv path 5 -> 6 + print("==== end ====") + + # TODO: unfused tiling should not separate last column + # test_ass_map(separated_tiling, unfused_separated_tiling) + + for i in range(6): # these counts should match! + print("====placed====") + terms = placed_fused_tiling.get_terms(i) + print(i, terms) + print( + "actual:", + len(list(unfused_placed_tiling.objects_of_size(i))), + ) + print("computed:", sum(k[0] * v for k, v in terms.items())) + print("====positive====") + terms = positive_fused_tiling.get_terms(i) + print(i, terms) + print( + "actual:", + len(list(unfused_positive_tiling.objects_of_size(i))), + ) + print("computed:", sum(k[0] * v for k, v in terms.items())) + print("====separated====") + terms = separated_tiling.get_terms(i) + print(i, terms) + print( + "actual:", + len(list(unfused_separated_tiling.objects_of_size(i))), + ) + print("computed:", sum(k[0] * v for k, v in terms.items())) + print() diff --git a/tilings/assumptions.py b/tilings/assumptions.py index 2086174d..d1e10520 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -1,7 +1,19 @@ import abc +from collections import defaultdict from importlib import import_module -from itertools import chain -from typing import TYPE_CHECKING, FrozenSet, Iterable, List, Optional, Tuple, Type +from itertools import chain, product +from typing import ( + TYPE_CHECKING, + DefaultDict, + Dict, + FrozenSet, + Iterable, + List, + Optional, + Set, + Tuple, + Type, +) from permuta import Perm @@ -13,65 +25,87 @@ from tilings import Tiling +class GriddingsCounter: + """Counts the number of griddings that the subgridded perm of a gp using + the active cells has onto the tiling with respect to the mapping. + The active cells are the values of the cell map.""" + + def __init__(self, tiling: "Tiling", cell_map: Dict[Cell, Cell]): + self.tiling = tiling + self.cell_map = cell_map + self._active_cells: Optional[FrozenSet[Cell]] = None + self.GP_CACHE: Dict[int, DefaultDict[GriddedPerm, Set[GriddedPerm]]] = {} + + @property + def active_cells(self) -> FrozenSet[Cell]: + if self._active_cells is None: + self._active_cells = frozenset( + self.cell_map[cell] for cell in self.tiling.active_cells + ) + return self._active_cells + + def _cell_map(self, cell: Cell) -> Cell: + return self.cell_map[cell] + + def _griddings(self, size: int) -> DefaultDict[GriddedPerm, Set[GriddedPerm]]: + if size not in self.GP_CACHE: + res: DefaultDict[GriddedPerm, Set[GriddedPerm]] = defaultdict(set) + for gp in self.tiling.objects_of_size(size): + mapped_gp = gp.apply_map(self._cell_map) + res[mapped_gp].add(gp) + self.GP_CACHE[size] = res + return self.GP_CACHE[size] + + def count_griddings(self, gp: GriddedPerm): + subgp = gp.get_gridded_perm_in_cells(self.active_cells) + return len(self._griddings(len(subgp))[subgp]) + + class TrackingAssumption: """ - An assumption used to keep track of the occurrences of a set of gridded - permutations. + An assumption used to keep track of the griddings of a tiling. """ - def __init__(self, gps: Iterable[GriddedPerm]): - self.gps = tuple(sorted(set(gps))) - - @classmethod - def from_cells(cls, cells: Iterable[Cell]) -> "TrackingAssumption": - gps = [GriddedPerm.single_cell((0,), cell) for cell in cells] - return TrackingAssumption(gps) - - def avoiding( + def __init__( + self, + tiling: "Tiling", + col_map: Dict[int, int], + row_map: Dict[int, int], + ): + self.tiling = tiling + self.col_map = col_map + self.row_map = row_map + self._cell_map: Optional[Dict[Cell, Cell]] = None + self.gridding_counter = GriddingsCounter(self.tiling, self.cell_map) + + @property + def cell_map(self) -> Dict[Cell, Cell]: + if self._cell_map is None: + self._cell_map = dict() + for (x1, x2), (y1, y2) in product( + self.col_map.items(), self.row_map.items() + ): + self._cell_map[(x1, y1)] = (x2, y2) + return self._cell_map + + def is_identity(self): + raise NotImplementedError + + def simplify( self, - obstructions: Iterable[GriddedPerm], - active_cells: Optional[Iterable[Cell]] = None, + tiling: "Tiling", ) -> "TrackingAssumption": """ - Return the tracking absumption where all of the gridded perms avoiding - the obstructions are removed. If active_cells is not None, then any - assumptions involving a cell not in active_cells will be removed. + Simplify the assumption according to it being on the given tiling. """ - obstructions = tuple(obstructions) - if active_cells is not None: - return self.__class__( - tuple( - gp - for gp in self.gps - if all(cell in active_cells for cell in gp.pos) - and gp.avoids(*obstructions) - ) - ) - return self.__class__(tuple(gp for gp in self.gps if gp.avoids(*obstructions))) + return self + raise NotImplementedError def get_value(self, gp: GriddedPerm) -> int: """ - Return the number of occurrences of each of the gridded perms being track in - the gridded perm gp. - """ - return len(list(chain.from_iterable(p.occurrences_in(gp) for p in self.gps))) - - def get_components(self, tiling: "Tiling") -> List[List[GriddedPerm]]: - """ - Return the lists of gps that count exactly one occurrence. - Only implemented for when a size one gp is in a point cell. - """ - return [ - [gp] for gp in self.gps if len(gp) == 1 and gp.pos[0] in tiling.point_cells - ] - - def remove_components(self, tiling: "Tiling") -> "TrackingAssumption": - """ - Return the TrackingAssumption found by removing all the components - found by the get_components method. + Return the number of griddings corresponding to gp. """ - gps_to_remove = set(chain.from_iterable(self.get_components(tiling))) - return self.__class__(gp for gp in self.gps if gp not in gps_to_remove) + return self.gridding_counter.count_griddings(gp) def to_jsonable(self) -> dict: """Return a dictionary form of the assumption.""" @@ -79,7 +113,9 @@ def to_jsonable(self) -> dict: return { "class_module": c.__module__, "assumption": c.__name__, - "gps": [gp.to_jsonable() for gp in self.gps], + "tiling": self.tiling.to_jsonable(), + "col_map": [(a, b) for a, b in self.col_map.items()], + "row_map": [(a, b) for a, b in self.row_map.items()], } @classmethod @@ -90,159 +126,49 @@ def from_dict(cls, d: dict) -> "TrackingAssumption": assert issubclass( AssClass, TrackingAssumption ), "Not a valid TrackingAssumption" - gps = [GriddedPerm.from_dict(gp) for gp in d["gps"]] - return AssClass(gps) + tiling = Tiling.from_dict(d["tiling"]) + row_map = {a: b for a, b in d["row_map"]} + col_map = {a: b for a, b in d["col_map"]} + return AssClass(tiling, col_map, row_map) def __eq__(self, other) -> bool: if other.__class__ == TrackingAssumption: - return bool(self.gps == other.gps) + return bool(self.tiling == other.tiling) and bool( + self.cell_map == other.cell_map + ) return NotImplemented def __lt__(self, other) -> bool: if isinstance(other, TrackingAssumption): - key_self = (self.__class__.__name__, self.gps) - key_other = (other.__class__.__name__, other.gps) + key_self = ( + self.__class__.__name__, + self.tiling.obstructions, + self.tiling.requirements, + tuple(sorted(self.cell_map.items())), + ) + key_other = ( + other.__class__.__name__, + other.tiling.obstructions, + other.tiling.requirements, + tuple(sorted(other.cell_map.items())), + ) return key_self < key_other return NotImplemented def __hash__(self) -> int: - return hash(self.gps) + return hash((self.tiling, tuple(sorted(self.cell_map.items())))) def __repr__(self) -> str: - return self.__class__.__name__ + "({})".format(self.gps) - - def __str__(self): - if all(len(gp) == 1 for gp in self.gps): - cells = ", ".join(str(gp.pos[0]) for gp in self.gps) - return f"can count points in cell{'s' if len(self.gps) > 1 else ''} {cells}" - return "can count occurrences of{}".format( - ", ".join(str(gp) for gp in self.gps) + return self.__class__.__name__ + "({}, {})".format( + repr(self.tiling), repr(self.cell_map) ) - -class ComponentAssumption(TrackingAssumption): - """ - An assumption used to keep track of the number of components in a - region of a tiling. - - In order to inherit from TrackingAssumption, the set of cells should be - given as a set of length 1 gridded perms using each cell. This ensures - most strategies work without change. - """ - - def __init__(self, gps: Iterable[GriddedPerm]): - super().__init__(gps) - assert all(len(gp) == 1 for gp in self.gps) - self.cells = frozenset(gp.pos[0] for gp in self.gps) - - @abc.abstractmethod - def decomposition(self, perm: Perm) -> List[Perm]: - """Count the number of component in a permutation.""" - - @abc.abstractmethod - def tiling_decomposition(self, tiling: "Tiling") -> List[List[Cell]]: - """Return the components of a given tiling.""" - - @abc.abstractmethod - def is_component( - self, - cells: List[Cell], - point_cells: FrozenSet[Cell], - positive_cells: FrozenSet[Cell], - ) -> bool: - """ - Return True if cells form a component. - """ - - def get_components(self, tiling: "Tiling") -> List[List[GriddedPerm]]: - sub_tiling = tiling.sub_tiling(self.cells) - separated_tiling, fwd_map = sub_tiling.row_and_column_separation_with_mapping() - back_map = {b: a for a, b in fwd_map.items()} - components = self.tiling_decomposition(separated_tiling) - return [ - [ - GriddedPerm.point_perm(sub_tiling.backward_cell_map[back_map[cell]]) - for cell in comp - ] - for comp in components - if self.is_component( - comp, separated_tiling.point_cells, separated_tiling.positive_cells - ) - ] - - def get_value(self, gp: GriddedPerm) -> int: - """ - Return the number of components in the tracked region of the gridded perm. - """ - subgp = gp.get_gridded_perm_in_cells(self.cells) - return len(self.decomposition(subgp.patt)) - - def __eq__(self, other) -> bool: - if isinstance(other, ComponentAssumption) and self.__class__ == other.__class__: - return bool(self.gps == other.gps) - return NotImplemented - - def __repr__(self) -> str: - return self.__class__.__name__ + "({})".format(self.gps) - - def __str__(self): - return f"can count components in cells {self.cells}" - - def __hash__(self) -> int: - return hash(self.gps) - - -class SumComponentAssumption(ComponentAssumption): - @staticmethod - def decomposition(perm: Perm) -> List[Perm]: - return perm.sum_decomposition() # type: ignore - - @staticmethod - def tiling_decomposition(tiling: "Tiling") -> List[List[Cell]]: - return tiling.sum_decomposition() - - @staticmethod - def is_component( - cells: List[Cell], point_cells: FrozenSet[Cell], positive_cells: FrozenSet[Cell] - ) -> bool: - if len(cells) == 2: - (x1, y1), (x2, y2) = sorted(cells) - if x1 != x2 and y1 > y2: # is skew - return all(cell in positive_cells for cell in cells) or any( - cell in point_cells for cell in cells - ) - return False - - def __str__(self): - return f"can count sum components in cells {self.cells}" - - def __hash__(self) -> int: - return hash(self.gps) - - -class SkewComponentAssumption(ComponentAssumption): - @staticmethod - def decomposition(perm: Perm) -> List[Perm]: - return perm.skew_decomposition() # type: ignore - - @staticmethod - def tiling_decomposition(tiling: "Tiling") -> List[List[Cell]]: - return tiling.skew_decomposition() - - @staticmethod - def is_component( - cells: List[Cell], point_cells: FrozenSet[Cell], positive_cells: FrozenSet[Cell] - ) -> bool: - if len(cells) == 2: - (x1, y1), (x2, y2) = sorted(cells) - if x1 != x2 and y1 < y2: # is sum - return all(cell in positive_cells for cell in cells) or any( - cell in point_cells for cell in cells - ) - return False - def __str__(self): - return f"can count skew components in cells {self.cells}" - - def __hash__(self) -> int: - return hash(self.gps) + map_str = "\n".join( + " {}: {}".format(c1, c2) for c1, c2 in sorted(self.cell_map.items()) + ) + tiling_str = " " + str(self.tiling).replace("\n", "\n ") + return ( + "Counting the griddings with respect to the " + + f"map\n{map_str}\non the tiling:\n{tiling_str}" + ) diff --git a/tilings/tiling.py b/tilings/tiling.py index 987c17fe..4ad43d96 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -44,11 +44,7 @@ SubobstructionInferral, guess_obstructions, ) -from .assumptions import ( - SkewComponentAssumption, - SumComponentAssumption, - TrackingAssumption, -) +from .assumptions import TrackingAssumption from .exception import InvalidOperationError from .griddedperm import GriddedPerm from .gui_launcher import run_gui @@ -342,9 +338,8 @@ def clean_assumptions(self) -> None: """ res: List[TrackingAssumption] = [] for assumption in self.assumptions: - ass = assumption.avoiding(self._obstructions, self.active_cells) - if ass.gps: - res.append(ass) + ass = assumption.simplify(self) + res.append(ass) self._assumptions = tuple(sorted(set(res))) @classmethod @@ -632,6 +627,22 @@ def add_requirement(self, patt: Perm, pos: Iterable[Cell]) -> "Tiling": new_req_list = (GriddedPerm(patt, pos),) return self.add_list_requirement(new_req_list) + def add_obstructions_and_requirements( + self, + obs: Iterable[GriddedPerm], + reqs: Iterable[Iterable[GriddedPerm]], + remove_empty_rows_and_cols=True, + ) -> "Tiling": + """Returns a new tiling with the obstructions and requirements added.""" + new_obs = tuple(obs) + new_reqs = tuple(tuple(gp) for gp in reqs) + return Tiling( + self._obstructions + new_obs, + self._requirements + new_reqs, + self._assumptions, + remove_empty_rows_and_cols=remove_empty_rows_and_cols, + ) + def add_single_cell_obstruction(self, patt: Perm, cell: Cell) -> "Tiling": """Returns a new tiling with the single cell obstruction of the pattern patt in the given cell.""" From fbcf7e8ef89ee906b3f88353258da1fbb3053263 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Tue, 14 Sep 2021 19:13:42 +0000 Subject: [PATCH 02/41] method to compute preimage --- tilings/algorithms/general_fusion.py | 42 +++----------- tilings/assumptions.py | 29 ++++++---- tilings/tiling.py | 87 +++++++++++++++++++--------- 3 files changed, 87 insertions(+), 71 deletions(-) diff --git a/tilings/algorithms/general_fusion.py b/tilings/algorithms/general_fusion.py index 1fbd1bf9..fe410f0f 100644 --- a/tilings/algorithms/general_fusion.py +++ b/tilings/algorithms/general_fusion.py @@ -156,44 +156,11 @@ def new_assumption(self) -> TrackingAssumption: def test_ass_map(tiling, original_tiling, verbose=False): - return if verbose: print("=" * 10) print("TESTING ASS MAP") print(tiling) print(original_tiling) - ass = tiling.assumptions[0] - original_obs, original_reqs = ass.obstructions_and_requirements() - new_obs = [] - for ob in tiling.obstructions: - if verbose: - print(ob) - for gp in ass.reverse_map(ob): - if gp.avoids(*original_obs): - if verbose: - print(" ", gp) - new_obs.append(gp) - new_reqs = [] - for req in tiling.requirements: - new_req = [] - for r in req: - if verbose: - print(r) - for gp in ass.reverse_map(r): - if any(gp.contains(*gps) for gps in original_reqs): - if verbose: - print(" ", gp) - new_req.append(gp) - new_reqs.append(new_req) - unfused = Tiling( - new_obs + original_obs, - new_reqs + original_reqs, - ) - if verbose: - # print(original_tiling) - print(unfused) - print("=" * 10) - assert unfused == original_tiling.remove_assumptions() for i in range(6): # these counts should match! terms = tiling.get_terms(i) actual = len(list(original_tiling.remove_assumptions().objects_of_size(i))) @@ -236,7 +203,7 @@ def test_ass_map(tiling, original_tiling, verbose=False): "assumptions": [], } ) - + print(tiling) gf = GeneralFusion(tiling, col_idx=0, tracked=True) print(gf.fusable()) print(gf.fused_tiling()) @@ -297,6 +264,13 @@ def test_ass_map(tiling, original_tiling, verbose=False): print("===== the fused tiling =====") print(fused_tiling) print("==== end ====") + for gp in sorted(fused_tiling.objects_of_size(3)): + print(gp) + for gp2 in sorted( + fused_tiling.assumptions[0].gridding_counter._griddings(3)[gp] + ): + print("\t", gp2) + assert 0 for i in range(6): terms = fused_tiling.get_terms(i) diff --git a/tilings/assumptions.py b/tilings/assumptions.py index d1e10520..409fce94 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -8,6 +8,7 @@ Dict, FrozenSet, Iterable, + Iterator, List, Optional, Set, @@ -77,6 +78,9 @@ def __init__( self.row_map = row_map self._cell_map: Optional[Dict[Cell, Cell]] = None self.gridding_counter = GriddingsCounter(self.tiling, self.cell_map) + self._ignore_reqs_gridding_counter = GriddingsCounter( + self.tiling.remove_requirements(), self.cell_map + ) @property def cell_map(self) -> Dict[Cell, Cell]: @@ -88,17 +92,22 @@ def cell_map(self) -> Dict[Cell, Cell]: self._cell_map[(x1, y1)] = (x2, y2) return self._cell_map - def is_identity(self): - raise NotImplementedError + def backward_map_gridded_perm( + self, gp: GriddedPerm, ignore_reqs: bool = False + ) -> Set[GriddedPerm]: + """Yield the gridded perms that map to gp according to the col and row maps.""" + if ignore_reqs: + return self._ignore_reqs_gridding_counter.GP_CACHE[len(gp)][gp] + return self.gridding_counter.GP_CACHE[len(gp)][gp] + + def forward_map_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: + """Map the gridded perm according to the col and row maps.""" + assert gp.avoids(*self.tiling.obstructions) and all( + gp.contains(*req) for req in self.tiling.requirements + ) + return gp.apply_map(self.gridding_counter._cell_map) - def simplify( - self, - tiling: "Tiling", - ) -> "TrackingAssumption": - """ - Simplify the assumption according to it being on the given tiling. - """ - return self + def is_identity(self): raise NotImplementedError def get_value(self, gp: GriddedPerm) -> int: diff --git a/tilings/tiling.py b/tilings/tiling.py index 4ad43d96..36f8cb25 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -596,42 +596,34 @@ def insert_cell(self, cell: Cell) -> "Tiling": ) return self.add_single_cell_requirement(Perm((0,)), cell) - def add_obstruction(self, patt: Perm, pos: Iterable[Cell]) -> "Tiling": + def add_obstruction( + self, patt: Perm, pos: Iterable[Cell], remove_empty_rows_and_cols: bool = True + ) -> "Tiling": """Returns a new tiling with the obstruction of the pattern patt with positions pos.""" - return Tiling( - self._obstructions + (GriddedPerm(patt, pos),), - self._requirements, - self._assumptions, + return self.add_obstructions( + (GriddedPerm(patt, pos),), + remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) - def add_obstructions(self, gps: Iterable[GriddedPerm]) -> "Tiling": + def add_obstructions( + self, gps: Iterable[GriddedPerm], remove_empty_rows_and_cols: bool = True + ) -> "Tiling": """Returns a new tiling with the obstructions added.""" new_obs = tuple(gps) return Tiling( - self._obstructions + new_obs, self._requirements, self._assumptions - ) - - def add_list_requirement(self, req_list: Iterable[GriddedPerm]) -> "Tiling": - """ - Return a new tiling with the requirement list added. - """ - new_req = tuple(req_list) - return Tiling( - self._obstructions, self._requirements + (new_req,), self._assumptions + self._obstructions + new_obs, + self._requirements, + self._assumptions, + self.active_cells, + remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) - def add_requirement(self, patt: Perm, pos: Iterable[Cell]) -> "Tiling": - """Returns a new tiling with the requirement of the pattern - patt with position pos.""" - new_req_list = (GriddedPerm(patt, pos),) - return self.add_list_requirement(new_req_list) - def add_obstructions_and_requirements( self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]], - remove_empty_rows_and_cols=True, + remove_empty_rows_and_cols: bool = True, ) -> "Tiling": """Returns a new tiling with the obstructions and requirements added.""" new_obs = tuple(obs) @@ -643,16 +635,57 @@ def add_obstructions_and_requirements( remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) - def add_single_cell_obstruction(self, patt: Perm, cell: Cell) -> "Tiling": + def add_list_requirement( + self, req_list: Iterable[GriddedPerm], remove_empty_rows_and_cols: bool = True + ) -> "Tiling": + """ + Return a new tiling with the requirement list added. + """ + new_req = tuple(req_list) + return Tiling( + self._obstructions, + self._requirements + (new_req,), + self._assumptions, + remove_empty_rows_and_cols=remove_empty_rows_and_cols, + ) + + def add_requirement( + self, + patt: Perm, + pos: Iterable[Cell], + remove_empty_rows_and_cols: bool = True, + ) -> "Tiling": + """Returns a new tiling with the requirement of the pattern + patt with position pos.""" + new_req_list = (GriddedPerm(patt, pos),) + return self.add_list_requirement( + new_req_list, + remove_empty_rows_and_cols=remove_empty_rows_and_cols, + ) + + def add_single_cell_obstruction( + self, + patt: Perm, + cell: Cell, + remove_empty_rows_and_cols: bool = True, + ) -> "Tiling": """Returns a new tiling with the single cell obstruction of the pattern patt in the given cell.""" - return self.add_obstructions((GriddedPerm.single_cell(patt, cell),)) + return self.add_obstructions( + (GriddedPerm.single_cell(patt, cell),), + remove_empty_rows_and_cols=remove_empty_rows_and_cols, + ) - def add_single_cell_requirement(self, patt: Perm, cell: Cell) -> "Tiling": + def add_single_cell_requirement( + self, patt: Perm, cell: Cell, remove_empty_rows_and_cols: bool = True + ) -> "Tiling": """Returns a new tiling with the single cell requirement of the pattern patt in the given cell.""" new_req_list = (GriddedPerm.single_cell(patt, cell),) - return self.add_list_requirement(new_req_list) + return self.add_list_requirement( + new_req_list, + remove_empty_rows_and_cols=remove_empty_rows_and_cols, + ) def add_assumption(self, assumption: TrackingAssumption) -> "Tiling": """Returns a new tiling with the added assumption.""" From fee9a24121e403a9cadba6dcd960934217c73f56 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 16 Sep 2021 13:05:01 +0000 Subject: [PATCH 03/41] add methods to clean empty rows and cols, add obstructions and requirements --- tilings/assumptions.py | 72 ++++++++++++++++++++++++++++++++++++ tilings/tiling.py | 83 ++++++++++++++++-------------------------- 2 files changed, 104 insertions(+), 51 deletions(-) diff --git a/tilings/assumptions.py b/tilings/assumptions.py index 409fce94..ce451d7c 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -73,15 +73,53 @@ def __init__( col_map: Dict[int, int], row_map: Dict[int, int], ): + assert not tiling.assumptions self.tiling = tiling self.col_map = col_map self.row_map = row_map + self.remove_empty_rows_and_cols() + self._init_checked() self._cell_map: Optional[Dict[Cell, Cell]] = None self.gridding_counter = GriddingsCounter(self.tiling, self.cell_map) self._ignore_reqs_gridding_counter = GriddingsCounter( self.tiling.remove_requirements(), self.cell_map ) + def _init_checked(self): + """ + Ensure that intervals are preseved with respect to row and col maps. + """ + cols = [b for _, b in sorted(self.col_map.items())] + assert cols == sorted(cols) + rows = [b for _, b in sorted(self.row_map.items())] + assert rows == sorted(rows) + + def remove_empty_rows_and_cols(self) -> None: + """ + Update the col and row maps after removing cols and rows that + became empty when tiling was created. + """ + self.col_map = { + self.tiling.forward_col_map[k]: v + for k, v in self.col_map.items() + if k in self.tiling.forward_col_map + } + self.row_map = { + self.tiling.forward_row_map[k]: v + for k, v in self.row_map.items() + if k in self.tiling.forward_row_map + } + + def apply_row_col_map( + self, row_mapping: Dict[int, int], col_mapping: Dict[int, int] + ): + """ + Update the col and row maps with respect to the given + row mapping and col mapping on the underlying tiling. + """ + self.col_map = {k: col_mapping[v] for k, v in self.col_map.items()} + self.row_map = {k: row_mapping[v] for k, v in self.row_map.items()} + @property def cell_map(self) -> Dict[Cell, Cell]: if self._cell_map is None: @@ -107,6 +145,40 @@ def forward_map_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: ) return gp.apply_map(self.gridding_counter._cell_map) + def add_obstructions(self, obs: Tuple[GriddedPerm, ...]) -> "TrackingAssumption": + new_obs = chain.from_iterable( + self.backward_map_gridded_perm(gp, False) for gp in obs + ) + return TrackingAssumption( + self.tiling.add_obstructions(new_obs), self.col_map, self.row_map + ) + + def add_obstructions_and_requirements( + self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]] + ) -> "TrackingAssumption": + new_obs = chain.from_iterable( + self.backward_map_gridded_perm(gp, False) for gp in obs + ) + new_reqs = chain.from_iterable( + chain.from_iterable(self.backward_map_gridded_perm(gp, False) for gp in req) + for req in reqs + ) + return TrackingAssumption( + self.tiling.add_obstructions_and_requirements(new_obs, new_reqs), + self.col_map, + self.row_map, + ) + + def add_list_requirement( + self, req_list: Iterable[GriddedPerm] + ) -> "TrackingAssumption": + new_req = chain.from_iterable( + self.backward_map_gridded_perm(gp, False) for gp in req_list + ) + return TrackingAssumption( + self.tiling.add_list_requirement(new_req), self.col_map, self.row_map + ) + def is_identity(self): raise NotImplementedError diff --git a/tilings/tiling.py b/tilings/tiling.py index 36f8cb25..da47f38e 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -92,7 +92,7 @@ def __init__( self, obstructions: Iterable[GriddedPerm] = tuple(), requirements: Iterable[Iterable[GriddedPerm]] = tuple(), - assumptions: Iterable[TrackingAssumption] = tuple(), + assumptions: Iterable[Iterable[TrackingAssumption]] = tuple(), remove_empty_rows_and_cols: bool = True, derive_empty: bool = True, simplify: bool = True, @@ -116,14 +116,14 @@ def __init__( # Set of requirement lists self._requirements = tuple(tuple(r) for r in requirements) # Set of assumptions - self._assumptions = tuple(assumptions) + self._assumptions = tuple(tuple(a) for a in assumptions) else: # Set of obstructions self._obstructions = tuple(sorted(obstructions)) # Set of requirement lists self._requirements = Tiling.sort_requirements(requirements) # Set of assumptions - self._assumptions = tuple(sorted(assumptions)) + self._assumptions = Tiling.sort_requirements(assumptions) # Simplify the set of obstructions and the set of requirement lists if simplify: @@ -131,10 +131,6 @@ def __init__( if not any(ob.is_empty() for ob in self.obstructions): - # Remove gridded perms that avoid obstructions from assumptions - if simplify: - self.clean_assumptions() - # Fill empty if derive_empty: if "empty_cells" not in self._cached_properties: @@ -247,6 +243,7 @@ def _remove_empty_rows_and_cols(self) -> None: self._cached_properties["forward_map"] = {} self._obstructions = (GriddedPerm.single_cell((0,), (0, 0)),) self._requirements = tuple() + assert not self._assumptions, "UH OH - we gotta think now" self._assumptions = tuple() self._cached_properties["dimensions"] = (1, 1) return @@ -285,13 +282,11 @@ def _remove_empty_rows_and_cols(self) -> None: tuple(req.apply_map(cell_map) for req in reqlist) for reqlist in self._requirements ) - self._assumptions = tuple( - sorted( - assumption.__class__( - tuple(gp.apply_map(cell_map) for gp in assumption.gps) - ) - for assumption in self._assumptions - ) + self._assumptions = Tiling.sort_assumptions( + [ + [ass.apply_row_col_map(row_mapping, col_mapping) for ass in assumption] + for assumption in self.assumptions + ] ) self._cached_properties["active_cells"] = frozenset( self._cached_properties["forward_map"][cell] @@ -329,19 +324,6 @@ def _minimize_mapping(self) -> Tuple[Dict[int, int], Dict[int, int], bool]: row_mapping = {y: actual for actual, y in enumerate(row_list)} return (col_mapping, row_mapping, False) - def clean_assumptions(self) -> None: - """ - Clean assumptions with respect to the known obstructions. - - TODO: this should remove points that are placed, and other requirements - that are contained in every gridded perm. - """ - res: List[TrackingAssumption] = [] - for assumption in self.assumptions: - ass = assumption.simplify(self) - res.append(ass) - self._assumptions = tuple(sorted(set(res))) - @classmethod def guess_from_gridded_perms( cls, gps: Iterable[GriddedPerm], max_len: int = -1 @@ -614,8 +596,10 @@ def add_obstructions( return Tiling( self._obstructions + new_obs, self._requirements, - self._assumptions, - self.active_cells, + [ + [ass.add_obstructions(new_obs) for ass in assumption] + for assumption in self._assumptions + ], remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) @@ -631,7 +615,13 @@ def add_obstructions_and_requirements( return Tiling( self._obstructions + new_obs, self._requirements + new_reqs, - self._assumptions, + [ + [ + ass.add_obstruction_and_requirements(new_obs, new_reqs) + for ass in assumption + ] + for assumption in self._assumptions + ], remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) @@ -645,7 +635,10 @@ def add_list_requirement( return Tiling( self._obstructions, self._requirements + (new_req,), - self._assumptions, + [ + [ass.add_list_requirement(new_req) for ass in assumption] + for assumption in self._assumptions + ], remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) @@ -702,11 +695,11 @@ def add_assumptions(self, assumptions: Iterable[TrackingAssumption]) -> "Tiling" simplify=False, sorted_input=True, ) - tiling.clean_assumptions() return tiling - def remove_assumption(self, assumption: TrackingAssumption): + def remove_assumption(self, assumption: Iterable[TrackingAssumption]): """Returns a new tiling with assumption removed.""" + assumption = tuple(sorted(set(assumption))) try: idx = self._assumptions.index(assumption) except ValueError as e: @@ -722,7 +715,6 @@ def remove_assumption(self, assumption: TrackingAssumption): simplify=False, sorted_input=True, ) - tiling.clean_assumptions() return tiling def remove_assumptions(self): @@ -911,6 +903,7 @@ def skew_decomposition(self) -> List[List[Cell]]: def sort_requirements( requirements: Iterable[Iterable[GriddedPerm]], ) -> Tuple[Tuple[GriddedPerm, ...], ...]: + # TODO: fix name and typing to allow for assumptions return tuple(sorted(tuple(sorted(set(reqlist))) for reqlist in requirements)) def backward_map(self, gp: GriddedPerm) -> GriddedPerm: @@ -920,24 +913,12 @@ def forward_map(self, gp: GriddedPerm) -> GriddedPerm: return GriddedPerm(gp.patt, [self.forward_cell_map[cell] for cell in gp.pos]) def forward_map_assumption( - self, assumption: TrackingAssumption, check_avoidance: bool = True - ) -> TrackingAssumption: + self, assumption: Iterable[TrackingAssumption] + ) -> Iterable[TrackingAssumption]: """ - Maps the assumption using the `forward_map` method on each gridded perm. - - If check_avoidance, it will return the assumption with only the mapped - gridded perms that avoid the obstructions on the tiling. + TODO: store forward row and col maps and use those! note: takes in iterable. """ - mapped_assumption = assumption.__class__( - tuple( - self.forward_map(gp) - for gp in assumption.gps - if all(cell in self.forward_cell_map for cell in gp.pos) - ) - ) - if check_avoidance: - return mapped_assumption.avoiding(self.obstructions) - return mapped_assumption + raise NotImplementedError @property def forward_cell_map(self) -> CellMap: @@ -1766,7 +1747,7 @@ def total_requirements(self) -> int: return len(self._requirements) @property - def assumptions(self) -> Tuple[TrackingAssumption, ...]: + def assumptions(self) -> Tuple[Tuple[TrackingAssumption, ...], ...]: return self._assumptions def total_assumptions(self) -> int: From 5bf4c93c1b0a367416c503c3d495f18eaf325cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Wed, 22 Sep 2021 04:36:13 -0400 Subject: [PATCH 04/41] Update tilings-assumption with develop (#352) * fix test that use expand_verified * Make the forward tiling map a class * add documentation * add a map_row and map_column method * update row map * fixing stuff I'm not sure I understand * remove dead stuff * good way to get preimage for grinding counter * Revert "fixing stuff I'm not sure I understand" This reverts commit 568845433da6a5b09ab9d6965c6599ff2980eff2. * remove some sum/skew import that no longer make sense * add remove requirements on tiling * fix a sort assumption not existing function * fix assumption map update * New tiling init map (#350) * Make the forward tiling map a class * add documentation * add a map_row and map_column method * remove print * cleaning after discussion with Christian * import sorting --- tests/test_assumptions.py | 6 +- tests/test_tiling.py | 7 +- tilings/algorithms/__init__.py | 2 + tilings/algorithms/map.py | 132 ++++++++++++++++++ tilings/algorithms/row_col_separation.py | 6 +- tilings/assumptions.py | 38 ++--- tilings/misc.py | 4 - tilings/strategies/assumption_splitting.py | 28 +--- tilings/strategies/factor.py | 6 +- tilings/strategies/fusion/fusion.py | 8 +- tilings/strategies/obstruction_inferral.py | 6 +- tilings/strategies/requirement_insertion.py | 10 +- tilings/strategies/requirement_placement.py | 14 +- tilings/strategies/verification.py | 1 - tilings/tiling.py | 147 +++++++++----------- 15 files changed, 249 insertions(+), 166 deletions(-) create mode 100644 tilings/algorithms/map.py diff --git a/tests/test_assumptions.py b/tests/test_assumptions.py index 5454ae31..0b00ca38 100644 --- a/tests/test_assumptions.py +++ b/tests/test_assumptions.py @@ -150,7 +150,7 @@ def test_123_fusion(): pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) css = TileScope("123", pack) spec = css.auto_search(status_update=30) - spec.expand_verified() + spec = spec.expand_verified() assert isinstance(spec, CombinatorialSpecification) assert [spec.count_objects_of_size(i) for i in range(20)] == [ 1, @@ -189,7 +189,7 @@ def test_123_positive_fusions(): ) css = TileScope("123", pack) spec = css.auto_search(status_update=30) - spec.expand_verified() + spec = spec.expand_verified() print(spec) assert isinstance(spec, CombinatorialSpecification) assert [spec.count_objects_of_size(i) for i in range(20)] == [ @@ -227,7 +227,7 @@ def test_123_interleaving(): pack = TileScopePack.point_placements().make_interleaving() css = TileScope("123", pack) spec = css.auto_search(status_update=30) - spec.expand_verified() + spec = spec.expand_verified() assert isinstance(spec, CombinatorialSpecification) assert [spec.count_objects_of_size(i) for i in range(20)] == [ 1, diff --git a/tests/test_tiling.py b/tests/test_tiling.py index ca8c799e..cbdc08cf 100644 --- a/tests/test_tiling.py +++ b/tests/test_tiling.py @@ -198,6 +198,7 @@ def test_constructor_no_requirements(typical_redundant_obstructions): derive_empty=False, simplify=False, ) + print(typical_redundant_obstructions) assert len(tiling._obstructions) == 20 assert len(tiling._requirements) == 0 (i, j) = tiling.dimensions @@ -2301,13 +2302,15 @@ def test_partial_place_col(obs_inf_til): def test_empty_obstruction(): t = Tiling((GriddedPerm.empty_perm(),)) - assert t.forward_cell_map == {} + assert t.forward_map._row_map == {} + assert t.forward_map._col_map == {} assert t.obstructions == (GriddedPerm.empty_perm(),) def test_point_obstruction(): t = Tiling((GriddedPerm((0,), ((0, 0),)),)) - assert t.forward_cell_map == {} + assert t.forward_map._row_map == {} + assert t.forward_map._col_map == {} assert t.obstructions == (GriddedPerm((0,), ((0, 0),)),) diff --git a/tilings/algorithms/__init__.py b/tilings/algorithms/__init__.py index baba63d2..ef70b8ac 100644 --- a/tilings/algorithms/__init__.py +++ b/tilings/algorithms/__init__.py @@ -4,6 +4,7 @@ from .gridded_perm_generation import GriddedPermsOnTiling from .gridded_perm_reduction import GriddedPermReduction from .guess_obstructions import guess_obstructions +from .map import RowColMap from .minimal_gridded_perms import MinimalGriddedPerms from .obstruction_inferral import ( AllObstructionInferral, @@ -34,6 +35,7 @@ "GriddedPermReduction", "RequirementPlacement", "RowColSeparation", + "RowColMap", "SubclassVerificationAlgorithm", "guess_obstructions", "Sliding", diff --git a/tilings/algorithms/map.py b/tilings/algorithms/map.py new file mode 100644 index 00000000..feaf2d21 --- /dev/null +++ b/tilings/algorithms/map.py @@ -0,0 +1,132 @@ +from typing import TYPE_CHECKING, Dict, Tuple + +from tilings.exception import InvalidOperationError + +if TYPE_CHECKING: + from tilings.assumptions import TrackingAssumption + from tilings.griddedperm import GriddedPerm + +Cell = Tuple[int, int] + + +class RowColMap: + """ + A class to combine a row and a column map together and map different object related + to tiling in accordance to those row and columns map. + + INPUT: + - `row_map`: the row map given as a dictionary. + - `col_map`: the column map given as a dictionary. + - `is_identity`: A boolean that indicate if the map is the identity. + """ + + def __init__( + self, row_map: Dict[int, int], col_map: Dict[int, int], is_identity: bool + ) -> None: + self._row_map = row_map + self._col_map = col_map + self._is_identity = is_identity + + @classmethod + def identity(cls, dimensions: Tuple[int, int]) -> "RowColMap": + """ + Build a map that is the identity for a tiling of the given dimensions. + + If one of the dimensions is 0 then the corresponding row/column map will + be an empty dictionary. + """ + col_map = {i: i for i in range(dimensions[0])} + row_map = {i: i for i in range(dimensions[1])} + return RowColMap(row_map=row_map, col_map=col_map, is_identity=True) + + def reverse(self) -> "RowColMap": + """ + Return the reverse map if possible. + Otherwise raise an InvalidOperationError. + """ + row_map = {v: k for k, v in self._row_map.items()} + col_map = {v: k for k, v in self._col_map.items()} + if len(row_map) != len(self._row_map) or len(col_map) != len(self._col_map): + raise InvalidOperationError("The map is not reversible.") + return RowColMap( + row_map=row_map, col_map=col_map, is_identity=self._is_identity + ) + + def is_identity(self) -> bool: + """ + Indicate if the map is the identity map. + """ + return self._is_identity + + def is_mappable_gp(self, gp: "GriddedPerm") -> bool: + """ + Return True if all the cell used by the gridded perm can be mapped. + """ + return all(self.is_mappable_cell(cell) for cell in gp.pos) + + def map_gp(self, gp: "GriddedPerm") -> "GriddedPerm": + """ + Map the gridded permutation according to the map. + """ + return gp.__class__(gp.patt, map(self.map_cell, gp.pos)) + + def map_assumption(self, assumption: "TrackingAssumption") -> "TrackingAssumption": + """ + Map the assumption according to the map. + + If some of the gridded permutation tracked by the assumption cannot be mapped + they are removed from the assumption. + """ + gps = tuple(self.map_gp(gp) for gp in assumption.gps if self.is_mappable_gp(gp)) + return assumption.__class__(gps) + + def is_mappable_cell(self, cell: Cell) -> bool: + """ + Return True if the cell can be mapped, i.e. if the image of the row + and the column of the are defined by the map. + """ + return self.is_mappable_col(cell[0]) and self.is_mappable_row(cell[1]) + + def map_cell(self, cell: Cell) -> Cell: + """ + Map the cell according to the map. + """ + return (self.map_col(cell[0]), self.map_row(cell[1])) + + def is_mappable_row(self, row: int) -> bool: + """ + Return True if the image of the row is defined. + """ + return row in self._row_map + + def map_row(self, row: int) -> int: + """ + Map the row according to the map. + """ + return self._row_map[row] + + def is_mappable_col(self, col: int) -> bool: + """ + Return True if the image of the column is defined. + """ + return col in self._col_map + + def map_col(self, col: int) -> int: + """ + Map the column according to the map. + """ + return self._col_map[col] + + def max_row(self) -> int: + """Return the biggest row index in the image.""" + return max(self._row_map.values()) + + def max_col(self) -> int: + """Return the biggest column index in the image.""" + return max(self._col_map.values()) + + def __str__(self) -> str: + s = "RowColMap\n" + s += f" row map: {self._row_map}\n" + s += f" col map: {self._col_map}\n" + return s diff --git a/tilings/algorithms/row_col_separation.py b/tilings/algorithms/row_col_separation.py index d8b1e982..8d4c92be 100644 --- a/tilings/algorithms/row_col_separation.py +++ b/tilings/algorithms/row_col_separation.py @@ -491,13 +491,13 @@ def get_cell_map(self) -> Dict[Cell, Cell]: row_order = self.max_row_order col_order = self.max_col_order sep_cell_map = self._get_cell_map(row_order, col_order) - init_cell_map = sep_tiling.forward_cell_map + init_cell_map = sep_tiling.forward_map res: Dict[Cell, Cell] = dict() for cell in self._tiling.active_cells: mid_cell = sep_cell_map[cell] # If the cell is not in the init map it is an empty cell - if mid_cell in init_cell_map: - final_cell = init_cell_map[mid_cell] + if init_cell_map.is_mappable_cell(mid_cell): + final_cell = init_cell_map.map_cell(mid_cell) res[cell] = final_cell return res diff --git a/tilings/assumptions.py b/tilings/assumptions.py index ce451d7c..26178755 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -23,7 +23,7 @@ Cell = Tuple[int, int] if TYPE_CHECKING: - from tilings import Tiling + from tilings.tiling import RowColMap, Tiling class GriddingsCounter: @@ -61,6 +61,9 @@ def count_griddings(self, gp: GriddedPerm): subgp = gp.get_gridded_perm_in_cells(self.active_cells) return len(self._griddings(len(subgp))[subgp]) + def preimage(self, gp: GriddedPerm) -> Set[GriddedPerm]: + return self._griddings(len(gp))[gp] + class TrackingAssumption: """ @@ -99,44 +102,41 @@ def remove_empty_rows_and_cols(self) -> None: Update the col and row maps after removing cols and rows that became empty when tiling was created. """ + forward_map = self.tiling.forward_map self.col_map = { - self.tiling.forward_col_map[k]: v + forward_map.map_col(k): v for k, v in self.col_map.items() - if k in self.tiling.forward_col_map + if forward_map.is_mappable_col(k) } self.row_map = { - self.tiling.forward_row_map[k]: v + forward_map.map_row(k): v for k, v in self.row_map.items() - if k in self.tiling.forward_row_map + if forward_map.is_mappable_row(k) } - def apply_row_col_map( - self, row_mapping: Dict[int, int], col_mapping: Dict[int, int] - ): + def apply_row_col_map(self, row_col_map: "RowColMap"): """ Update the col and row maps with respect to the given row mapping and col mapping on the underlying tiling. """ - self.col_map = {k: col_mapping[v] for k, v in self.col_map.items()} - self.row_map = {k: row_mapping[v] for k, v in self.row_map.items()} + self.col_map = {k: row_col_map.map_col(v) for k, v in self.col_map.items()} + self.row_map = {k: row_col_map.map_row(v) for k, v in self.row_map.items()} + return self @property def cell_map(self) -> Dict[Cell, Cell]: - if self._cell_map is None: - self._cell_map = dict() - for (x1, x2), (y1, y2) in product( - self.col_map.items(), self.row_map.items() - ): - self._cell_map[(x1, y1)] = (x2, y2) - return self._cell_map + cell_map: Dict[Cell, Cell] = dict() + for (x1, x2), (y1, y2) in product(self.col_map.items(), self.row_map.items()): + cell_map[(x1, y1)] = (x2, y2) + return cell_map def backward_map_gridded_perm( self, gp: GriddedPerm, ignore_reqs: bool = False ) -> Set[GriddedPerm]: """Yield the gridded perms that map to gp according to the col and row maps.""" if ignore_reqs: - return self._ignore_reqs_gridding_counter.GP_CACHE[len(gp)][gp] - return self.gridding_counter.GP_CACHE[len(gp)][gp] + return self._ignore_reqs_gridding_counter.preimage(gp) + return self.gridding_counter.preimage(gp) def forward_map_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: """Map the gridded perm according to the col and row maps.""" diff --git a/tilings/misc.py b/tilings/misc.py index f9d56d94..4958db5d 100644 --- a/tilings/misc.py +++ b/tilings/misc.py @@ -11,10 +11,6 @@ Cell = Tuple[int, int] -def map_cell(col_mapping: Dict[int, int], row_mapping: Dict[int, int], cell: Cell): - return (col_mapping[cell[0]], row_mapping[cell[1]]) - - def union_reduce(iterables: Iterable[Iterable[T]]) -> Set[T]: """ Returns the union of the elements contained in the iterables. diff --git a/tilings/strategies/assumption_splitting.py b/tilings/strategies/assumption_splitting.py index 4943db47..38ee7919 100644 --- a/tilings/strategies/assumption_splitting.py +++ b/tilings/strategies/assumption_splitting.py @@ -20,11 +20,7 @@ from comb_spec_searcher.utils import compositions from tilings import GriddedPerm, Tiling from tilings.algorithms import factor -from tilings.assumptions import ( - SkewComponentAssumption, - SumComponentAssumption, - TrackingAssumption, -) +from tilings.assumptions import TrackingAssumption Cell = Tuple[int, int] @@ -216,28 +212,6 @@ def _split_tracking_assumption( return [assumption] return [assumption.__class__(gps) for gps in split_gps if gps] - def _split_skew_assumption( - self, assumption: SkewComponentAssumption - ) -> List[TrackingAssumption]: - decomposition = self.skew_decomposition(assumption.cells) - return [ - SkewComponentAssumption( - GriddedPerm.single_cell((0,), cell) for cell in cells - ) - for cells in decomposition - ] - - def _split_sum_assumption( - self, assumption: SumComponentAssumption - ) -> List[TrackingAssumption]: - decomposition = self.sum_decomposition(assumption.cells) - return [ - SumComponentAssumption( - GriddedPerm.single_cell((0,), cell) for cell in cells - ) - for cells in decomposition - ] - @staticmethod def sum_decomposition( cells: Iterable[Cell], skew: bool = False diff --git a/tilings/strategies/factor.py b/tilings/strategies/factor.py index c84cb113..9692da29 100644 --- a/tilings/strategies/factor.py +++ b/tilings/strategies/factor.py @@ -70,7 +70,7 @@ def extra_parameters( ): for idx, child in enumerate(children): # TODO: consider skew/sum - new_assumption = child.forward_map_assumption(assumption) + new_assumption = child.forward_map.map_assumption(assumption) if new_assumption.gps: child_var = child.get_assumption_parameter(new_assumption) extra_parameters[idx][parent_var] = child_var @@ -96,7 +96,7 @@ def backward_map( if children is None: children = self.decomposition_function(tiling) gps_to_combine = tuple( - tiling.backward_map(cast(GriddedPerm, gp)) + tiling.backward_map.map_gp(cast(GriddedPerm, gp)) for gp, tiling in zip(gps, children) ) temp = [ @@ -118,7 +118,7 @@ def forward_map( if children is None: children = self.decomposition_function(tiling) return tuple( - tiling.forward_map(gp.get_gridded_perm_in_cells(part)) + tiling.forward_map.map_gp(gp.get_gridded_perm_in_cells(part)) for tiling, part in zip(children, self.partition) ) diff --git a/tilings/strategies/fusion/fusion.py b/tilings/strategies/fusion/fusion.py index 7082fa1b..bb9d2e99 100644 --- a/tilings/strategies/fusion/fusion.py +++ b/tilings/strategies/fusion/fusion.py @@ -251,7 +251,7 @@ def extra_parameters( algo = self.fusion_algorithm(comb_class) child = children[0] mapped_assumptions = [ - child.forward_map_assumption(ass.__class__(gps)) + child.forward_map.map_assumption(ass.__class__(gps)) for ass, gps in zip(comb_class.assumptions, algo.assumptions_fuse_counters) ] return ( @@ -289,7 +289,7 @@ def _fuse_parameter(self, comb_class: Tiling) -> str: algo = self.fusion_algorithm(comb_class) child = algo.fused_tiling() ass = algo.new_assumption() - fuse_assumption = ass.__class__(child.forward_map(gp) for gp in ass.gps) + fuse_assumption = ass.__class__(child.forward_map.map_gp(gp) for gp in ass.gps) return child.get_assumption_parameter(fuse_assumption) def formal_step(self) -> str: @@ -313,7 +313,7 @@ def backward_map( children = self.decomposition_function(comb_class) gp = objs[0] assert gp is not None - gp = children[0].backward_map(gp) + gp = children[0].backward_map.map_gp(gp) yield from self.fusion_algorithm(comb_class).unfuse_gridded_perm( gp, left_points ) @@ -331,7 +331,7 @@ def forward_map( if children is None: children = self.decomposition_function(comb_class) fused_gp = self.fusion_algorithm(comb_class).fuse_gridded_perm(obj) - return (children[0].forward_map(fused_gp),) + return (children[0].forward_map.map_gp(fused_gp),) def to_jsonable(self) -> dict: d = super().to_jsonable() diff --git a/tilings/strategies/obstruction_inferral.py b/tilings/strategies/obstruction_inferral.py index 67a832ee..5bc5ecb1 100644 --- a/tilings/strategies/obstruction_inferral.py +++ b/tilings/strategies/obstruction_inferral.py @@ -48,7 +48,7 @@ def extra_parameters( child = children[0] params: Dict[str, str] = {} for assumption in comb_class.assumptions: - mapped_assumption = child.forward_map_assumption(assumption) + mapped_assumption = child.forward_map.map_assumption(assumption) if mapped_assumption.gps: parent_var = comb_class.get_assumption_parameter(assumption) child_var = child.get_assumption_parameter(mapped_assumption) @@ -63,7 +63,7 @@ def backward_map( ) -> Iterator[GriddedPerm]: if children is None: children = self.decomposition_function(tiling) - yield children[0].backward_map(cast(GriddedPerm, gps[0])) + yield children[0].backward_map.map_gp(cast(GriddedPerm, gps[0])) def forward_map( self, @@ -73,7 +73,7 @@ def forward_map( ) -> Tuple[GriddedPerm]: if children is None: children = self.decomposition_function(tiling) - return (children[0].forward_map(gp),) + return (children[0].forward_map.map_gp(gp),) def __repr__(self) -> str: return self.__class__.__name__ + "(gps={})".format(self.gps) diff --git a/tilings/strategies/requirement_insertion.py b/tilings/strategies/requirement_insertion.py index f6035271..4f6a0a92 100644 --- a/tilings/strategies/requirement_insertion.py +++ b/tilings/strategies/requirement_insertion.py @@ -57,7 +57,7 @@ def backward_map( if children is None: children = self.decomposition_function(tiling) idx = DisjointUnionStrategy.backward_map_index(gps) - yield children[idx].backward_map(cast(GriddedPerm, gps[idx])) + yield children[idx].backward_map.map_gp(cast(GriddedPerm, gps[idx])) def forward_map( self, @@ -68,8 +68,8 @@ def forward_map( if children is None: children = self.decomposition_function(tiling) if gp.avoids(*self.gps): - return (children[0].forward_map(gp), None) - return (None, children[1].forward_map(gp)) + return (children[0].forward_map.map_gp(gp), None) + return (None, children[1].forward_map.map_gp(gp)) def extra_parameters( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None @@ -85,11 +85,11 @@ def extra_parameters( co_params: Dict[str, str] = {} for assumption in comb_class.assumptions: parent_var = comb_class.get_assumption_parameter(assumption) - av_mapped_assumption = av.forward_map_assumption(assumption) + av_mapped_assumption = av.forward_map.map_assumption(assumption) if av_mapped_assumption.gps: child_var = av.get_assumption_parameter(av_mapped_assumption) av_params[parent_var] = child_var - co_mapped_assumption = co.forward_map_assumption(assumption) + co_mapped_assumption = co.forward_map.map_assumption(assumption) if co_mapped_assumption.gps: child_var = co.get_assumption_parameter(co_mapped_assumption) co_params[parent_var] = child_var diff --git a/tilings/strategies/requirement_placement.py b/tilings/strategies/requirement_placement.py index 2c88af18..04d831ad 100644 --- a/tilings/strategies/requirement_placement.py +++ b/tilings/strategies/requirement_placement.py @@ -91,9 +91,9 @@ def extra_parameters( if self.include_empty: child = children[0] for assumption in comb_class.assumptions: - mapped_assumption = child.forward_map_assumption(assumption).avoiding( - child.obstructions - ) + mapped_assumption = child.forward_map.map_assumption( + assumption + ).avoiding(child.obstructions) if mapped_assumption.gps: parent_var = comb_class.get_assumption_parameter(assumption) child_var = child.get_assumption_parameter(mapped_assumption) @@ -102,7 +102,7 @@ def extra_parameters( zip(self._placed_cells, children[1:] if self.include_empty else children) ): mapped_assumptions = [ - child.forward_map_assumption(ass) + child.forward_map.map_assumption(ass).avoiding(child.obstructions) for ass in algo.stretched_assumptions(cell) ] for assumption, mapped_assumption in zip( @@ -192,7 +192,7 @@ def backward_map( if children is None: children = self.decomposition_function(tiling) idx = DisjointUnionStrategy.backward_map_index(gps) - gp: GriddedPerm = children[idx].backward_map(cast(GriddedPerm, gps[idx])) + gp: GriddedPerm = children[idx].backward_map.map_gp(cast(GriddedPerm, gps[idx])) if self.include_empty: if idx == 0: yield gp @@ -213,7 +213,7 @@ def forward_map( if children is None: children = self.decomposition_function(tiling) if indices is None: - return (children[0].forward_map(gp),) + tuple( + return (children[0].forward_map.map_gp(gp),) + tuple( None for _ in range(len(children) - 1) ) gps_index, forced_index = indices @@ -223,7 +223,7 @@ def forward_map( gp = self.forward_gp_map(gp, forced_index) return ( tuple(None for _ in range(child_index)) - + (children[child_index].forward_map(gp),) + + (children[child_index].forward_map.map_gp(gp),) + tuple(None for _ in range(len(children) - 1)) ) diff --git a/tilings/strategies/verification.py b/tilings/strategies/verification.py index e2986ecc..41f90608 100644 --- a/tilings/strategies/verification.py +++ b/tilings/strategies/verification.py @@ -26,7 +26,6 @@ LocalEnumeration, MonotoneTreeEnumeration, ) -from tilings.assumptions import ComponentAssumption from tilings.strategies import ( FactorFactory, FactorInsertionFactory, diff --git a/tilings/tiling.py b/tilings/tiling.py index da47f38e..a4f4d614 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -2,7 +2,7 @@ import json from array import array from collections import Counter, defaultdict -from functools import partial, reduce +from functools import reduce from itertools import chain, filterfalse, product from operator import mul, xor from typing import ( @@ -39,6 +39,7 @@ MinimalGriddedPerms, ObstructionTransitivity, RequirementPlacement, + RowColMap, RowColSeparation, SubclassVerificationAlgorithm, SubobstructionInferral, @@ -48,28 +49,27 @@ from .exception import InvalidOperationError from .griddedperm import GriddedPerm from .gui_launcher import run_gui -from .misc import intersection_reduce, map_cell, union_reduce +from .misc import intersection_reduce, union_reduce __all__ = ["Tiling"] Cell = Tuple[int, int] ReqList = Tuple[GriddedPerm, ...] - CellBasis = Dict[Cell, Tuple[List[Perm], List[Perm]]] -CellMap = Dict[Cell, Cell] CellFrozenSet = FrozenSet[Cell] Dimension = Tuple[int, int] + CachedProperties = TypedDict( "CachedProperties", { "active_cells": CellFrozenSet, - "backward_map": CellMap, + "backward_map": RowColMap, "cell_basis": CellBasis, "dimensions": Dimension, "empty_cells": CellFrozenSet, - "forward_map": CellMap, + "forward_map": RowColMap, "point_cells": CellFrozenSet, "positive_cells": CellFrozenSet, "possibly_empty": CellFrozenSet, @@ -145,11 +145,11 @@ def __init__( self._requirements = tuple() self._assumptions = tuple() self._cached_properties["active_cells"] = frozenset() - self._cached_properties["backward_map"] = {} + self._cached_properties["backward_map"] = RowColMap.identity((0, 0)) self._cached_properties["cell_basis"] = {(0, 0): ([Perm()], [])} self._cached_properties["dimensions"] = (1, 1) self._cached_properties["empty_cells"] = frozenset([(0, 0)]) - self._cached_properties["forward_map"] = {} + self._cached_properties["forward_map"] = RowColMap.identity((0, 0)) self._cached_properties["point_cells"] = frozenset() self._cached_properties["positive_cells"] = frozenset() self._cached_properties["possibly_empty"] = frozenset() @@ -240,70 +240,49 @@ def _remove_empty_rows_and_cols(self) -> None: # Produce the mapping between the two tilings if not self.active_cells: assert GriddedPerm.empty_perm() not in self.obstructions - self._cached_properties["forward_map"] = {} + self._cached_properties["forward_map"] = RowColMap.identity((0, 0)) self._obstructions = (GriddedPerm.single_cell((0,), (0, 0)),) self._requirements = tuple() assert not self._assumptions, "UH OH - we gotta think now" self._assumptions = tuple() self._cached_properties["dimensions"] = (1, 1) return - col_mapping, row_mapping, identity = self._minimize_mapping() - cell_map = partial(map_cell, col_mapping, row_mapping) - - if identity: - self._cached_properties["forward_map"] = { - cell: cell for cell in self.active_cells - } - # We still may need to remove point obstructions if the empty row or col - # was on the end! - (width, height) = self.dimensions - self._obstructions = tuple( - ob - for ob in self.obstructions - if len(ob) > 1 or (ob.pos[0][0] < width and ob.pos[0][1] < height) - ) - return - - # For tracking regions. - self._cached_properties["forward_map"] = { - (k_x, k_y): (v_x, v_y) - for k_x, v_x in col_mapping.items() - for k_y, v_y in row_mapping.items() - } - new_obs = [] - for ob in self._obstructions: - cell = ob.pos[0] - if not ob.is_point_perm() or ( - cell[0] in col_mapping and cell[1] in row_mapping - ): - new_obs.append(ob.apply_map(cell_map)) - self._obstructions = tuple(new_obs) - self._requirements = tuple( - tuple(req.apply_map(cell_map) for req in reqlist) - for reqlist in self._requirements - ) - self._assumptions = Tiling.sort_assumptions( - [ - [ass.apply_row_col_map(row_mapping, col_mapping) for ass in assumption] - for assumption in self.assumptions - ] - ) - self._cached_properties["active_cells"] = frozenset( - self._cached_properties["forward_map"][cell] - for cell in self._cached_properties["active_cells"] - if cell in self._cached_properties["forward_map"] - ) - self._cached_properties["empty_cells"] = frozenset( - self._cached_properties["forward_map"][cell] - for cell in self._cached_properties["empty_cells"] - if cell in self._cached_properties["forward_map"] - ) - self._cached_properties["dimensions"] = ( - max(col_mapping.values()) + 1, - max(row_mapping.values()) + 1, + forward_map = self._minimize_mapping() + self._cached_properties["forward_map"] = forward_map + # We still may need to remove point obstructions if the empty row or col + # was on the end so we do it outside the next if statement. + self._obstructions = tuple( + forward_map.map_gp(ob) + for ob in self.obstructions + if not ob.is_point_perm() or forward_map.is_mappable_gp(ob) ) + if not forward_map.is_identity(): + self._requirements = tuple( + tuple(forward_map.map_gp(req) for req in reqlist) + for reqlist in self._requirements + ) + self._assumptions = Tiling.sort_requirements( + [ + [ass.apply_row_col_map(forward_map) for ass in assumption] + for assumption in self.assumptions + ] + ) + self._cached_properties["active_cells"] = frozenset( + forward_map.map_cell(cell) + for cell in self._cached_properties["active_cells"] + if forward_map.is_mappable_cell(cell) + ) + self._cached_properties["empty_cells"] = frozenset( + forward_map.map_cell(cell) + for cell in self._cached_properties["empty_cells"] + if forward_map.is_mappable_cell(cell) + ) + self._cached_properties["dimensions"] = ( + forward_map.max_col() + 1, + forward_map.max_row() + 1, + ) - def _minimize_mapping(self) -> Tuple[Dict[int, int], Dict[int, int], bool]: + def _minimize_mapping(self) -> RowColMap: """ Returns a pair of dictionaries, that map rows/columns to an equivalent set of rows/columns where empty ones have been removed. @@ -318,11 +297,9 @@ def _minimize_mapping(self) -> Tuple[Dict[int, int], Dict[int, int], bool]: identity = (self.dimensions[0] == len(col_list)) and ( self.dimensions[1] == len(row_list) ) - if identity: - return ({}, {}, True) col_mapping = {x: actual for actual, x in enumerate(col_list)} row_mapping = {y: actual for actual, y in enumerate(row_list)} - return (col_mapping, row_mapping, False) + return RowColMap(row_map=row_mapping, col_map=col_mapping, is_identity=identity) @classmethod def guess_from_gridded_perms( @@ -730,6 +707,20 @@ def remove_assumptions(self): sorted_input=True, ) + def remove_requirements(self): + """ + Return the same tiling without the requirements. + """ + assert not self.assumptions, "Now think" + return self.__class__( + obstructions=self._obstructions, + requirements=[], + remove_empty_rows_and_cols=False, + derive_empty=False, + simplify=False, + sorted_input=True, + ) + def remove_components_from_assumptions(self): """ Return the tiling with all the actual components from individual @@ -906,22 +897,8 @@ def sort_requirements( # TODO: fix name and typing to allow for assumptions return tuple(sorted(tuple(sorted(set(reqlist))) for reqlist in requirements)) - def backward_map(self, gp: GriddedPerm) -> GriddedPerm: - return GriddedPerm(gp.patt, [self.backward_cell_map[cell] for cell in gp.pos]) - - def forward_map(self, gp: GriddedPerm) -> GriddedPerm: - return GriddedPerm(gp.patt, [self.forward_cell_map[cell] for cell in gp.pos]) - - def forward_map_assumption( - self, assumption: Iterable[TrackingAssumption] - ) -> Iterable[TrackingAssumption]: - """ - TODO: store forward row and col maps and use those! note: takes in iterable. - """ - raise NotImplementedError - @property - def forward_cell_map(self) -> CellMap: + def forward_map(self) -> RowColMap: try: return self._cached_properties["forward_map"] except KeyError: @@ -929,11 +906,11 @@ def forward_cell_map(self) -> CellMap: return self._cached_properties["forward_map"] @property - def backward_cell_map(self) -> CellMap: + def backward_map(self) -> RowColMap: try: return self._cached_properties["backward_map"] except KeyError: - backward_map = {b: a for a, b in self.forward_cell_map.items()} + backward_map = self.forward_map.reverse() self._cached_properties["backward_map"] = backward_map return backward_map @@ -2008,7 +1985,7 @@ def __str__(self) -> str: result.append("\n") for i, ass in enumerate(self.assumptions): result.append("Assumption {}:\n".format(str(i))) - result.append(str(ass)) + result.extend(map(str, ass)) result.append("\n") if self.assumptions or self.requirements: result = result[:-1] From 8b4df8f6e22c88ba6329e88d72c7c0756438546a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 23 Sep 2021 07:25:01 -0400 Subject: [PATCH 05/41] new assumption cell map (#355) * make is_identity param optional in map * add compose function for map * add __eq__ to map * make map hashable * make the tracking assumption use the cell map * move map outside of algo * adding test for non-crossing map Also using it in the init check of tracking assumption * improve map string * fix map is_identity --- tilings/algorithms/__init__.py | 2 - tilings/assumptions.py | 147 ++++++++++---------------------- tilings/{algorithms => }/map.py | 48 ++++++++++- tilings/tiling.py | 2 +- 4 files changed, 92 insertions(+), 107 deletions(-) rename tilings/{algorithms => }/map.py (69%) diff --git a/tilings/algorithms/__init__.py b/tilings/algorithms/__init__.py index ef70b8ac..baba63d2 100644 --- a/tilings/algorithms/__init__.py +++ b/tilings/algorithms/__init__.py @@ -4,7 +4,6 @@ from .gridded_perm_generation import GriddedPermsOnTiling from .gridded_perm_reduction import GriddedPermReduction from .guess_obstructions import guess_obstructions -from .map import RowColMap from .minimal_gridded_perms import MinimalGriddedPerms from .obstruction_inferral import ( AllObstructionInferral, @@ -35,7 +34,6 @@ "GriddedPermReduction", "RequirementPlacement", "RowColSeparation", - "RowColMap", "SubclassVerificationAlgorithm", "guess_obstructions", "Sliding", diff --git a/tilings/assumptions.py b/tilings/assumptions.py index 26178755..0ef25229 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -4,6 +4,7 @@ from itertools import chain, product from typing import ( TYPE_CHECKING, + Callable, DefaultDict, Dict, FrozenSet, @@ -19,11 +20,12 @@ from permuta import Perm from .griddedperm import GriddedPerm +from .map import RowColMap Cell = Tuple[int, int] if TYPE_CHECKING: - from tilings.tiling import RowColMap, Tiling + from tilings.tiling import Tiling class GriddingsCounter: @@ -31,7 +33,7 @@ class GriddingsCounter: the active cells has onto the tiling with respect to the mapping. The active cells are the values of the cell map.""" - def __init__(self, tiling: "Tiling", cell_map: Dict[Cell, Cell]): + def __init__(self, tiling: "Tiling", cell_map: Callable[[Cell], Cell]): self.tiling = tiling self.cell_map = cell_map self._active_cells: Optional[FrozenSet[Cell]] = None @@ -41,18 +43,15 @@ def __init__(self, tiling: "Tiling", cell_map: Dict[Cell, Cell]): def active_cells(self) -> FrozenSet[Cell]: if self._active_cells is None: self._active_cells = frozenset( - self.cell_map[cell] for cell in self.tiling.active_cells + self.cell_map(cell) for cell in self.tiling.active_cells ) return self._active_cells - def _cell_map(self, cell: Cell) -> Cell: - return self.cell_map[cell] - def _griddings(self, size: int) -> DefaultDict[GriddedPerm, Set[GriddedPerm]]: if size not in self.GP_CACHE: res: DefaultDict[GriddedPerm, Set[GriddedPerm]] = defaultdict(set) for gp in self.tiling.objects_of_size(size): - mapped_gp = gp.apply_map(self._cell_map) + mapped_gp = gp.apply_map(self.cell_map) res[mapped_gp].add(gp) self.GP_CACHE[size] = res return self.GP_CACHE[size] @@ -73,63 +72,38 @@ class TrackingAssumption: def __init__( self, tiling: "Tiling", - col_map: Dict[int, int], - row_map: Dict[int, int], + row_col_map: RowColMap, ): assert not tiling.assumptions self.tiling = tiling - self.col_map = col_map - self.row_map = row_map + self.map = row_col_map self.remove_empty_rows_and_cols() self._init_checked() - self._cell_map: Optional[Dict[Cell, Cell]] = None - self.gridding_counter = GriddingsCounter(self.tiling, self.cell_map) + self.gridding_counter = GriddingsCounter(self.tiling, self.map.map_cell) self._ignore_reqs_gridding_counter = GriddingsCounter( - self.tiling.remove_requirements(), self.cell_map + self.tiling.remove_requirements(), self.map.map_cell ) def _init_checked(self): """ Ensure that intervals are preseved with respect to row and col maps. """ - cols = [b for _, b in sorted(self.col_map.items())] - assert cols == sorted(cols) - rows = [b for _, b in sorted(self.row_map.items())] - assert rows == sorted(rows) + assert self.map.is_non_crossing() def remove_empty_rows_and_cols(self) -> None: """ Update the col and row maps after removing cols and rows that became empty when tiling was created. """ - forward_map = self.tiling.forward_map - self.col_map = { - forward_map.map_col(k): v - for k, v in self.col_map.items() - if forward_map.is_mappable_col(k) - } - self.row_map = { - forward_map.map_row(k): v - for k, v in self.row_map.items() - if forward_map.is_mappable_row(k) - } - - def apply_row_col_map(self, row_col_map: "RowColMap"): + self.map = self.tiling.backward_map.compose(self.map) + + def apply_row_col_map(self, row_col_map: "RowColMap") -> "TrackingAssumption": """ - Update the col and row maps with respect to the given - row mapping and col mapping on the underlying tiling. + Modify in place the map with respect to the given row_col_map. Return self. """ - self.col_map = {k: row_col_map.map_col(v) for k, v in self.col_map.items()} - self.row_map = {k: row_col_map.map_row(v) for k, v in self.row_map.items()} + self.map = self.map.compose(row_col_map) return self - @property - def cell_map(self) -> Dict[Cell, Cell]: - cell_map: Dict[Cell, Cell] = dict() - for (x1, x2), (y1, y2) in product(self.col_map.items(), self.row_map.items()): - cell_map[(x1, y1)] = (x2, y2) - return cell_map - def backward_map_gridded_perm( self, gp: GriddedPerm, ignore_reqs: bool = False ) -> Set[GriddedPerm]: @@ -143,15 +117,13 @@ def forward_map_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: assert gp.avoids(*self.tiling.obstructions) and all( gp.contains(*req) for req in self.tiling.requirements ) - return gp.apply_map(self.gridding_counter._cell_map) + return self.map.map_gp(gp) def add_obstructions(self, obs: Tuple[GriddedPerm, ...]) -> "TrackingAssumption": new_obs = chain.from_iterable( self.backward_map_gridded_perm(gp, False) for gp in obs ) - return TrackingAssumption( - self.tiling.add_obstructions(new_obs), self.col_map, self.row_map - ) + return TrackingAssumption(self.tiling.add_obstructions(new_obs), self.map) def add_obstructions_and_requirements( self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]] @@ -164,9 +136,7 @@ def add_obstructions_and_requirements( for req in reqs ) return TrackingAssumption( - self.tiling.add_obstructions_and_requirements(new_obs, new_reqs), - self.col_map, - self.row_map, + self.tiling.add_obstructions_and_requirements(new_obs, new_reqs), self.map ) def add_list_requirement( @@ -175,9 +145,7 @@ def add_list_requirement( new_req = chain.from_iterable( self.backward_map_gridded_perm(gp, False) for gp in req_list ) - return TrackingAssumption( - self.tiling.add_list_requirement(new_req), self.col_map, self.row_map - ) + return TrackingAssumption(self.tiling.add_list_requirement(new_req), self.map) def is_identity(self): raise NotImplementedError @@ -190,64 +158,43 @@ def get_value(self, gp: GriddedPerm) -> int: def to_jsonable(self) -> dict: """Return a dictionary form of the assumption.""" - c = self.__class__ - return { - "class_module": c.__module__, - "assumption": c.__name__, - "tiling": self.tiling.to_jsonable(), - "col_map": [(a, b) for a, b in self.col_map.items()], - "row_map": [(a, b) for a, b in self.row_map.items()], - } + raise NotImplementedError @classmethod def from_dict(cls, d: dict) -> "TrackingAssumption": """Return the assumption from the json dict representation.""" - module = import_module(d["class_module"]) - AssClass: Type["TrackingAssumption"] = getattr(module, d["assumption"]) - assert issubclass( - AssClass, TrackingAssumption - ), "Not a valid TrackingAssumption" - tiling = Tiling.from_dict(d["tiling"]) - row_map = {a: b for a, b in d["row_map"]} - col_map = {a: b for a, b in d["col_map"]} - return AssClass(tiling, col_map, row_map) - - def __eq__(self, other) -> bool: - if other.__class__ == TrackingAssumption: - return bool(self.tiling == other.tiling) and bool( - self.cell_map == other.cell_map - ) - return NotImplemented - - def __lt__(self, other) -> bool: - if isinstance(other, TrackingAssumption): - key_self = ( - self.__class__.__name__, - self.tiling.obstructions, - self.tiling.requirements, - tuple(sorted(self.cell_map.items())), - ) - key_other = ( - other.__class__.__name__, - other.tiling.obstructions, - other.tiling.requirements, - tuple(sorted(other.cell_map.items())), - ) - return key_self < key_other - return NotImplemented + raise NotImplementedError + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TrackingAssumption): + return NotImplemented + return self.tiling == other.tiling and self.map == other.map + + def __lt__(self, other: object) -> bool: + if not isinstance(other, TrackingAssumption): + return NotImplemented + key_self = ( + self.__class__.__name__, + self.tiling.obstructions, + self.tiling.requirements, + self.map, + ) + key_other = ( + other.__class__.__name__, + other.tiling.obstructions, + other.tiling.requirements, + self.map, + ) + return key_self < key_other def __hash__(self) -> int: - return hash((self.tiling, tuple(sorted(self.cell_map.items())))) + return hash((self.tiling, self.map)) def __repr__(self) -> str: - return self.__class__.__name__ + "({}, {})".format( - repr(self.tiling), repr(self.cell_map) - ) + return f"{self.__class__.__name__}({self.tiling!r}), {self.map!r})" def __str__(self): - map_str = "\n".join( - " {}: {}".format(c1, c2) for c1, c2 in sorted(self.cell_map.items()) - ) + map_str = " " + str(self.map).replace("\n", "\n ") tiling_str = " " + str(self.tiling).replace("\n", "\n ") return ( "Counting the griddings with respect to the " diff --git a/tilings/algorithms/map.py b/tilings/map.py similarity index 69% rename from tilings/algorithms/map.py rename to tilings/map.py index feaf2d21..2e6d4d71 100644 --- a/tilings/algorithms/map.py +++ b/tilings/map.py @@ -1,4 +1,5 @@ -from typing import TYPE_CHECKING, Dict, Tuple +import itertools +from typing import TYPE_CHECKING, Dict, Optional, Tuple from tilings.exception import InvalidOperationError @@ -21,7 +22,10 @@ class RowColMap: """ def __init__( - self, row_map: Dict[int, int], col_map: Dict[int, int], is_identity: bool + self, + row_map: Dict[int, int], + col_map: Dict[int, int], + is_identity: Optional[bool] = None, ) -> None: self._row_map = row_map self._col_map = col_map @@ -52,12 +56,34 @@ def reverse(self) -> "RowColMap": row_map=row_map, col_map=col_map, is_identity=self._is_identity ) + def compose(self, other: "RowColMap") -> "RowColMap": + """ + The return the new map that is obtained by the applying first self and then + other. + + If self maps a -> b and other maps b -> c than the resulting map maps a -> c. + """ + col_map = {k: other.map_col(v) for k, v in self._col_map.items()} + row_map = {k: other.map_row(v) for k, v in self._row_map.items()} + return RowColMap(row_map=row_map, col_map=col_map) + def is_identity(self) -> bool: """ Indicate if the map is the identity map. """ + if self._is_identity is None: + kv_pairs = itertools.chain(self._col_map.items(), self._row_map.items()) + self._is_identity = all(k == v for k, v in kv_pairs) return self._is_identity + def is_non_crossing(self) -> bool: + """ + Check that the row map and col map map interval to interval. + """ + cols = [b for _, b in sorted(self._col_map.items())] + rows = [b for _, b in sorted(self._row_map.items())] + return cols == sorted(cols) and rows == sorted(rows) + def is_mappable_gp(self, gp: "GriddedPerm") -> bool: """ Return True if all the cell used by the gridded perm can be mapped. @@ -127,6 +153,20 @@ def max_col(self) -> int: def __str__(self) -> str: s = "RowColMap\n" - s += f" row map: {self._row_map}\n" - s += f" col map: {self._col_map}\n" + rows = [f"{k}: {v}" for k, v in sorted(self._row_map.items())] + cols = [f"{k}: {v}" for k, v in sorted(self._col_map.items())] + row_str = ", ".join(rows) + col_str = ", ".join(cols) + s += f" row map: {{{row_str}}}\n" + s += f" col map: {{{col_str}}}" return s + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RowColMap): + return NotImplemented + return self._col_map == other._col_map and self._row_map == other._row_map + + def __hash__(self) -> int: + row_map = tuple(sorted(self._row_map.items())) + col_map = tuple(sorted(self._col_map.items())) + return hash((col_map, row_map)) diff --git a/tilings/tiling.py b/tilings/tiling.py index 05153927..e32e2b1c 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -39,7 +39,6 @@ MinimalGriddedPerms, ObstructionTransitivity, RequirementPlacement, - RowColMap, RowColSeparation, SubclassVerificationAlgorithm, SubobstructionInferral, @@ -49,6 +48,7 @@ from .exception import InvalidOperationError from .griddedperm import GriddedPerm from .gui_launcher import run_gui +from .map import RowColMap from .misc import intersection_reduce, union_reduce __all__ = ["Tiling"] From 3a08dc81075b6a92ab772bed230aefa3d943aac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Tue, 28 Sep 2021 06:45:40 -0400 Subject: [PATCH 06/41] Param counter (#356) * fix some test for new assumption * shell of new ParameterCounter and PreimageCounter * making Tiling class work with the new param counter * probably fixed the html display * some doc and cleaning to preimage counter and param counter * fix tiling test to use new param counter * map can compute the preimage * add tilings contain method * implement preimage method on PreimageCounter * bunch of dunder method for PreimageCounter * clear assumption file * assumptions -> parameters in the main file * fix factor algorithm * fix row col algo * fix req placement * fix tilings tests * fix enumeration algorithm * fix counter * fix tilescope * ignore all broken file for mypy * fix guess obstruction test * fix algorithm test * xfail all broken test * fix some flake8 * ignore some flake8 error * fix small typo --- mypy.ini | 16 + tests/algorithms/test_enumeration.py | 8 + tests/algorithms/test_fusion.py | 2 + tests/algorithms/test_guess_obstructions.py | 11 - .../algorithms/test_requirement_placements.py | 8 +- tests/algorithms/test_row_col_separation.py | 1 - tests/test_bijections.py | 2 + tests/test_map.py | 62 ++++ tests/test_tilescope.py | 2 + tests/test_tiling.py | 83 ++--- tilings/algorithms/enumeration.py | 17 +- tilings/algorithms/factor.py | 43 +-- tilings/algorithms/general_fusion.py | 8 +- tilings/algorithms/requirement_placement.py | 36 +- tilings/algorithms/row_col_separation.py | 20 +- tilings/assumptions.py | 200 +---------- tilings/map.py | 40 ++- tilings/parameter_counter.py | 179 ++++++++++ tilings/tilescope.py | 4 +- tilings/tiling.py | 333 ++++++------------ tox.ini | 12 +- 21 files changed, 509 insertions(+), 578 deletions(-) create mode 100644 tests/test_map.py create mode 100644 tilings/parameter_counter.py diff --git a/mypy.ini b/mypy.ini index 34a4b00a..8831bb1e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -9,3 +9,19 @@ ignore_errors = True [mypy-sympy.*,pymongo.*,pytest.*,logzero.*,importlib_metadata.*] ignore_missing_imports = True + +# Temporary addition. Should be removed before going in develop +[mypy-tilings.strategies.*] +ignore_errors = True + +[mypy-tilings.algorithms.fusion] +ignore_errors = True + +[mypy-tilings.algorithms.general_fusion] +ignore_errors = True + +[mypy-tilings.algorithms.sliding] +ignore_errors = True + +[mypy-tilings.bijections] +ignore_errors = True diff --git a/tests/algorithms/test_enumeration.py b/tests/algorithms/test_enumeration.py index 4a17469c..637a1d2c 100644 --- a/tests/algorithms/test_enumeration.py +++ b/tests/algorithms/test_enumeration.py @@ -252,6 +252,7 @@ def test_not_verified(self, enum_with_list_req, onebyone_enum, enum_with_crossin ) assert not MonotoneTreeEnumeration(forest_tiling).verified() + @pytest.mark.xfail def test_get_genf(self, enum_verified): x = sympy.Symbol("x") expected_gf = -( @@ -271,6 +272,7 @@ def test_get_genf(self, enum_verified): expected_gf = -1 / ((x - 1) * (x / (x - 1) + 1)) assert sympy.simplify(enum_no_start.get_genf() - expected_gf) == 0 + @pytest.mark.xfail def test_get_genf_simple(self): t = Tiling( obstructions=[ @@ -283,6 +285,7 @@ def test_get_genf_simple(self): assert enum.verified() assert sympy.simplify(enum.get_genf() - sympy.sympify("1/(1-2*x)")) == 0 + @pytest.mark.xfail def test_with_finite_monotone_cell(self): t = Tiling( obstructions=[ @@ -297,6 +300,7 @@ def test_with_finite_monotone_cell(self): assert enum.verified() assert enum.get_genf().expand() == sympy.sympify("1+2*x+2*x**2") + @pytest.mark.xfail def test_with_finite_monotone_cell2(self): t = Tiling( obstructions=[ @@ -363,6 +367,7 @@ def test_interleave_fixed_lengths(self, enum_verified): + 20 * x ** 11 * dummy_var ** 3 * cell_var ** 3 ) + @pytest.mark.xfail def test_genf_with_req(self): t = Tiling( obstructions=[ @@ -381,6 +386,7 @@ def test_genf_with_req(self): terms = [0, 0, 0, 3, 10, 25, 56, 119, 246, 501, 1012] assert taylor_expand(genf) == terms + @pytest.mark.xfail def test_genf_with_big_finite_cell(self): t = Tiling( obstructions=[ @@ -406,6 +412,7 @@ def test_genf_with_big_finite_cell(self): + 20 * x ** 6 ) + @pytest.mark.xfail def test_with_two_reqs(self): t = Tiling( obstructions=( @@ -425,6 +432,7 @@ def test_with_two_reqs(self): assert enum.verified() assert taylor_expand(enum.get_genf()) == expected_enum + @pytest.mark.xfail def test_corner(self): t = Tiling( obstructions=( diff --git a/tests/algorithms/test_fusion.py b/tests/algorithms/test_fusion.py index 0c153b86..768cc6c7 100644 --- a/tests/algorithms/test_fusion.py +++ b/tests/algorithms/test_fusion.py @@ -5,6 +5,8 @@ from tilings.algorithms import ComponentFusion, Fusion from tilings.assumptions import TrackingAssumption +pytestmark = pytest.mark.xfail + class TestFusion: @pytest.fixture diff --git a/tests/algorithms/test_guess_obstructions.py b/tests/algorithms/test_guess_obstructions.py index 3eb81724..e10e6a3d 100644 --- a/tests/algorithms/test_guess_obstructions.py +++ b/tests/algorithms/test_guess_obstructions.py @@ -6,16 +6,12 @@ def test_guess_obstruction(): ( Tiling( obstructions=(GriddedPerm((0, 1), ((0, 0), (0, 0))),), - requirements=(), - assumptions=(), ), 5, ), ( Tiling( obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))),), - requirements=(), - assumptions=(), ), 3, ), @@ -29,7 +25,6 @@ def test_guess_obstruction(): GriddedPerm((2, 1, 0, 3), ((0, 0), (1, 0), (1, 0), (2, 0))), GriddedPerm((2, 0, 1, 3), ((0, 0), (1, 0), (1, 0), (2, 0))), ), - requirements=(), ), 4, ), @@ -55,8 +50,6 @@ def test_guess_obstruction(): (0, 4, 2, 1, 3), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0)) ), ), - requirements=(), - assumptions=(), ), 5, ), @@ -74,8 +67,6 @@ def test_guess_obstruction(): GriddedPerm((2, 1, 0), ((2, 0), (2, 0), (2, 0))), GriddedPerm((3, 2, 1, 0), ((1, 1), (2, 0), (2, 0), (2, 0))), ), - requirements=(), - assumptions=(), ), 4, ), @@ -95,8 +86,6 @@ def test_guess_obstruction(): ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), ), ), - requirements=(), - assumptions=(), ), 7, ), diff --git a/tests/algorithms/test_requirement_placements.py b/tests/algorithms/test_requirement_placements.py index 7853a200..2498f5eb 100644 --- a/tests/algorithms/test_requirement_placements.py +++ b/tests/algorithms/test_requirement_placements.py @@ -382,10 +382,10 @@ def test_stretched_requirements(placement1, placement1owncol, placement1ownrow): ) -def test_stretched_obstructions_and_assumptions( +def test_stretched_obstructions_and_parameters( placement1, placement1owncol, placement1ownrow ): - obs, reqs, _ = placement1._stretched_obstructions_requirements_and_assumptions( + obs, reqs, _ = placement1._stretched_obstructions_requirements_and_parameters( (1, 1) ) assert set(obs) == set( @@ -402,7 +402,7 @@ def test_stretched_obstructions_and_assumptions( obs, reqs, _, - ) = placement1ownrow._stretched_obstructions_requirements_and_assumptions((1, 1)) + ) = placement1ownrow._stretched_obstructions_requirements_and_parameters((1, 1)) assert set(obs) == set( placement1ownrow.stretched_obstructions((1, 1)) + [ @@ -418,7 +418,7 @@ def test_stretched_obstructions_and_assumptions( obs, reqs, _, - ) = placement1owncol._stretched_obstructions_requirements_and_assumptions((1, 1)) + ) = placement1owncol._stretched_obstructions_requirements_and_parameters((1, 1)) assert set(obs) == set( placement1owncol.stretched_obstructions((1, 1)) + [ diff --git a/tests/algorithms/test_row_col_separation.py b/tests/algorithms/test_row_col_separation.py index b8e2a416..08d7460b 100644 --- a/tests/algorithms/test_row_col_separation.py +++ b/tests/algorithms/test_row_col_separation.py @@ -896,7 +896,6 @@ def test_backmap(): GriddedPerm((2, 3, 0, 1), ((0, 1), (0, 2), (1, 0), (1, 0))), ), requirements=((GriddedPerm((0,), ((1, 2),)),),), - assumptions=(), ) cellmap1 = { (0, 1): (0, 1), diff --git a/tests/test_bijections.py b/tests/test_bijections.py index f1f3c07d..c724a080 100644 --- a/tests/test_bijections.py +++ b/tests/test_bijections.py @@ -28,6 +28,8 @@ from tilings.strategies.sliding import SlidingFactory, SlidingStrategy from tilings.tilescope import TileScope, TileScopePack +pytestmark = pytest.mark.xfail + def find_bijection_between( searcher1: CombinatorialSpecificationSearcher, diff --git a/tests/test_map.py b/tests/test_map.py new file mode 100644 index 00000000..46a6d310 --- /dev/null +++ b/tests/test_map.py @@ -0,0 +1,62 @@ +import pytest + +from tilings.griddedperm import GriddedPerm +from tilings.map import RowColMap + + +@pytest.fixture +def double_row_map(): + return RowColMap(row_map={0: 0, 1: 0}, col_map={0: 0}) + + +@pytest.fixture +def double_col_map(): + return RowColMap(col_map={0: 0, 1: 0}, row_map={0: 0}) + + +@pytest.fixture +def double_all_map(): + return RowColMap(col_map={0: 0, 1: 0}, row_map={0: 0, 1: 0}) + + +def test_preimage_row(double_row_map, double_col_map, double_all_map): + assert sorted(double_row_map.preimage_row(0)) == [0, 1] + assert sorted(double_col_map.preimage_row(0)) == [0] + assert sorted(double_all_map.preimage_row(0)) == [0, 1] + + +def test_preimage_col(double_row_map, double_col_map, double_all_map): + assert sorted(double_row_map.preimage_col(0)) == [0] + assert sorted(double_col_map.preimage_col(0)) == [0, 1] + assert sorted(double_all_map.preimage_col(0)) == [0, 1] + + +def test_preimage_cell(double_row_map, double_col_map, double_all_map): + assert sorted(double_row_map.preimage_cell((0, 0))) == [(0, 0), (0, 1)] + assert sorted(double_col_map.preimage_cell((0, 0))) == [(0, 0), (1, 0)] + assert sorted(double_all_map.preimage_cell((0, 0))) == [ + (0, 0), + (0, 1), + (1, 0), + (1, 1), + ] + + +def test_preimage_gp(double_all_map): + assert sorted(double_all_map.preimage_gp(GriddedPerm((0,), ((0, 0),)))) == [ + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((0, 1),)), + GriddedPerm((0,), ((1, 0),)), + GriddedPerm((0,), ((1, 1),)), + ] + assert sorted(double_all_map.preimage_gp(GriddedPerm((0, 1), ((0, 0),) * 2))) == [ + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (0, 1))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (1, 1))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 1))), + GriddedPerm((0, 1), ((1, 1), (1, 1))), + ] diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index 77d427ab..6b2125af 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -12,6 +12,8 @@ from tilings.strategy_pack import TileScopePack from tilings.tilescope import GuidedSearcher, TileScope +pytestmark = pytest.mark.xfail + point_placements = TileScopePack.point_placements() all_the_strategies_verify_database = TileScopePack.all_the_strategies().make_database() all_the_strategies_fusion = TileScopePack.all_the_strategies().make_fusion( diff --git a/tests/test_tiling.py b/tests/test_tiling.py index 1a44eed6..37ba00ca 100644 --- a/tests/test_tiling.py +++ b/tests/test_tiling.py @@ -8,8 +8,9 @@ from permuta import Perm from tilings import GriddedPerm, Tiling from tilings.algorithms import Fusion as FusionAlg -from tilings.assumptions import TrackingAssumption from tilings.exception import InvalidOperationError +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter @pytest.fixture @@ -493,7 +494,7 @@ def test_json(compresstil): # For backward compatibility make sure we can load from json that don't have # the assumptions field d = compresstil.to_jsonable() - d.pop("assumptions") + d.pop("parameters") assert compresstil == Tiling.from_json(json.dumps(d)) @@ -1395,26 +1396,37 @@ def test_is_monotone_cell(isolated_tiling): def test_repr(factorable_tiling, empty_tiling): assert factorable_tiling == eval(repr(factorable_tiling)) assert empty_tiling == eval(repr(empty_tiling)) - assert repr(Tiling()) == "Tiling(obstructions=(), requirements=(), assumptions=())" + assert repr(Tiling()) == "Tiling(obstructions=(), requirements=(), parameters=())" def test_initial_conditions(empty_tiling, finite_tiling): assert empty_tiling.initial_conditions(4) == [0, 0, 0, 0, 0] assert finite_tiling.initial_conditions(6) == [0, 1, 3, 6, 5, 0, 0] + ass_t = Tiling( + obstructions=[ + GriddedPerm((0, 1), ((0, 0),) * 2), + GriddedPerm((0, 1), ((0, 1),) * 2), + GriddedPerm((0, 1), ((0, 2),) * 2), + GriddedPerm((0, 1), ((0, 1), (0, 2))), + ], + ) + param_counter = ParameterCounter( + [PreimageCounter(ass_t, RowColMap(col_map={0: 0}, row_map={0: 0, 1: 1, 2: 1}))] + ) with_ass = Tiling( obstructions=[ GriddedPerm((0, 1), ((0, 0),) * 2), GriddedPerm((0, 1), ((0, 1),) * 2), ], - assumptions=[TrackingAssumption([GriddedPerm((0,), ((0, 1),))])], + parameters=[param_counter], ) assert with_ass.initial_conditions(5) == [ - 1, - sympy.sympify("1+k_0"), - sympy.sympify("1+2*k_0+k_0**2"), - sympy.sympify("k_0**3 + 3*k_0**2 + 3*k_0 + 1"), - sympy.sympify("k_0**4 + 4*k_0**3 + 6*k_0**2 + 4*k_0 + 1"), - sympy.sympify("k_0**5 + 5*k_0**4 + 10*k_0**3 + 10*k_0**2 + 5*k_0 + 1"), + sympy.sympify("k_0"), + sympy.sympify("k_0+k_0**2"), + sympy.sympify("k_0+2*k_0**2+k_0**3"), + sympy.sympify("k_0**4 + 3*k_0**3 + 3*k_0**2 + k_0"), + sympy.sympify("k_0**5 + 4*k_0**4 + 6*k_0**3 + 4*k_0**2 + k_0"), + sympy.sympify("k_0**6 + 5*k_0**5 + 10*k_0**4 + 10*k_0**3 + 5*k_0**2 + k_0"), ] @@ -1423,6 +1435,7 @@ def test_initial_conditions(empty_tiling, finite_tiling): # ------------------------------------------------------------ +@pytest.mark.xfail def test_fusion(): t = Tiling( obstructions=[ @@ -1459,12 +1472,12 @@ def test_fusion(): GriddedPerm(Perm((2, 0, 1)), [(0, 2), (0, 1), (0, 2)]), ], assumptions=[ - TrackingAssumption( - [ - GriddedPerm.single_cell((0,), (0, 1)), - GriddedPerm.single_cell((0,), (0, 2)), - ] - ) + # TrackingAssumption( + # [ + # GriddedPerm.single_cell((0,), (0, 1)), + # GriddedPerm.single_cell((0,), (0, 2)), + # ] + # ) ], ) assert FusionAlg(t2, row_idx=0, tracked=True, isolation_level=None).fusable() @@ -1473,6 +1486,7 @@ def test_fusion(): ).fusable() +@pytest.mark.xfail def test_component_fusion(): t = Tiling( obstructions=[ @@ -2329,7 +2343,6 @@ def test_is_atom(): (GriddedPerm((0,), ((1, 1),)),), (GriddedPerm((0,), ((2, 2),)),), ), - assumptions=(), ) empty_perm = Tiling() empty_set = Tiling((GriddedPerm.empty_perm(),)) @@ -2338,6 +2351,7 @@ def test_is_atom(): assert not empty_set.is_atom() +@pytest.mark.xfail class TestGetGenf: """ Group all the test regarding getting the generating function for a tiling. @@ -2412,7 +2426,6 @@ def test_enmerate_gp_up_to(): GriddedPerm((0, 2, 1), ((2, 0), (2, 0), (2, 0))), ), requirements=((GriddedPerm((0,), ((1, 2),)),),), - assumptions=(), ).enmerate_gp_up_to(8) == [0, 1, 2, 5, 14, 42, 132, 429, 1430] ) @@ -2431,7 +2444,6 @@ def test_column_reverse(): (GriddedPerm((0,), ((1, 1),)),), (GriddedPerm((1, 0), ((0, 2), (0, 0))),), ), - assumptions=(), ).column_reverse(0) == Tiling( obstructions=( GriddedPerm((0, 1), ((1, 1), (1, 1))), @@ -2444,7 +2456,6 @@ def test_column_reverse(): (GriddedPerm((0,), ((1, 1),)),), (GriddedPerm((0, 1), ((0, 0), (0, 2))),), ), - assumptions=(), ) t = Tiling( obstructions=( @@ -2469,7 +2480,6 @@ def test_column_reverse(): (GriddedPerm((0,), ((0, 1),)),), (GriddedPerm((0,), ((1, 2),)), GriddedPerm((0,), ((2, 0),))), ), - assumptions=(), ) assert ( Counter(len(gp) for gp in t.gridded_perms(7)) @@ -2488,7 +2498,6 @@ def test_row_complement(): GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), ), requirements=((GriddedPerm((2, 1, 0), ((1, 1), (1, 0), (1, 0))),),), - assumptions=(), ).row_complement(0) == Tiling( obstructions=( GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (1, 1))), @@ -2497,7 +2506,6 @@ def test_row_complement(): GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), ), requirements=((GriddedPerm((2, 0, 1), ((1, 1), (1, 0), (1, 0))),),), - assumptions=(), ) t = Tiling( obstructions=( @@ -2522,7 +2530,6 @@ def test_row_complement(): (GriddedPerm((0,), ((0, 1),)),), (GriddedPerm((0,), ((1, 2),)), GriddedPerm((0,), ((2, 0),))), ), - assumptions=(), ) assert ( Counter(len(gp) for gp in t.gridded_perms(7)) @@ -2543,7 +2550,6 @@ def test_permute_columns(): GriddedPerm((2, 0, 1, 3), ((0, 1), (0, 1), (0, 1), (1, 2))), ), requirements=((GriddedPerm((1, 0), ((0, 2), (0, 0))),),), - assumptions=(), ).permute_columns((2, 0, 1)) == Tiling( obstructions=( GriddedPerm((1, 0), ((0, 0), (0, 0))), @@ -2554,7 +2560,6 @@ def test_permute_columns(): GriddedPerm((2, 0, 1, 3), ((1, 1), (1, 1), (1, 1), (2, 2))), ), requirements=((GriddedPerm((1, 0), ((1, 2), (1, 0))),),), - assumptions=(), ) @@ -2569,7 +2574,6 @@ def test_permute_rows(): GriddedPerm((0, 2, 1), ((1, 1), (1, 2), (1, 1))), ), requirements=(), - assumptions=(), ).permute_rows((1, 2, 0)) == Tiling( obstructions=( GriddedPerm((1, 0), ((1, 2), (1, 2))), @@ -2579,8 +2583,6 @@ def test_permute_rows(): GriddedPerm((1, 0, 2), ((0, 2), (1, 1), (1, 2))), GriddedPerm((0, 2, 1), ((1, 0), (1, 1), (1, 0))), ), - requirements=(), - assumptions=(), ) @@ -2600,7 +2602,6 @@ def test_apply_perm_map_to_cell(): GriddedPerm((1, 0, 3, 2), ((2, 2), (2, 2), (2, 2), (2, 2))), ), requirements=((GriddedPerm((0,), ((2, 1),)),),), - assumptions=(), ).apply_perm_map_to_cell(lambda p: p.complement(), (0, 2)) == Tiling( obstructions=( GriddedPerm((1, 0), ((1, 0), (1, 0))), @@ -2620,29 +2621,22 @@ def test_apply_perm_map_to_cell(): GriddedPerm((3, 0, 2, 1), ((0, 2), (0, 2), (0, 2), (2, 2))), ), requirements=((GriddedPerm((0,), ((2, 1),)),),), - assumptions=(), ) def test_contains_all_patterns_locally_for_crossing(): - t = Tiling(obstructions=(), requirements=(), assumptions=()) + t = Tiling(obstructions=(), requirements=(), parameters=()) assert t.contains_all_patterns_locally_for_crossing((0, 0)) t = Tiling( obstructions=(GriddedPerm((0,), ((0, 0),)),), - requirements=(), - assumptions=(), ) assert t.contains_all_patterns_locally_for_crossing((0, 0)) t = Tiling( obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))),), - requirements=(), - assumptions=(), ) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in range(3)) t = Tiling( obstructions=(GriddedPerm((0, 1, 2, 3), ((0, 0), (1, 0), (1, 0), (2, 0))),), - requirements=(), - assumptions=(), ) assert not t.contains_all_patterns_locally_for_crossing((1, 0)) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (0, 2)) @@ -2651,8 +2645,6 @@ def test_contains_all_patterns_locally_for_crossing(): GriddedPerm((0, 1, 2, 3), ((0, 0), (1, 0), (1, 0), (2, 0))), GriddedPerm((0, 2, 1, 3), ((0, 0), (1, 0), (1, 0), (2, 0))), ), - requirements=(), - assumptions=(), ) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in range(3)) t = Tiling( @@ -2663,8 +2655,6 @@ def test_contains_all_patterns_locally_for_crossing(): GriddedPerm((0, 2, 4, 1, 3), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0))), GriddedPerm((0, 4, 1, 2, 3), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0))), ), - requirements=(), - assumptions=(), ) assert not t.contains_all_patterns_locally_for_crossing((1, 0)) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (0, 2)) @@ -2677,8 +2667,6 @@ def test_contains_all_patterns_locally_for_crossing(): GriddedPerm((0, 4, 1, 2, 3), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0))), GriddedPerm((0, 4, 2, 1, 3), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0))), ), - requirements=(), - assumptions=(), ) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in range(3)) t = Tiling( @@ -2693,8 +2681,6 @@ def test_contains_all_patterns_locally_for_crossing(): (0, 1, 2, 3, 4, 5), ((0, 0), (1, 0), (1, 0), (1, 0), (2, 0), (2, 0)) ), ), - requirements=(), - assumptions=(), ) assert t.contains_all_patterns_locally_for_crossing((0, 0)) assert not any(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (1, 2)) @@ -2715,22 +2701,18 @@ def test_contains_all_patterns_locally_for_crossing(): ((0, 0), (0, 0), (1, 0), (1, 0), (2, 0), (2, 0), (2, 0)), ), ), - requirements=(), - assumptions=(), ) assert t.contains_all_patterns_locally_for_crossing((1, 0)) assert not any(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (0, 2)) t = Tiling( obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))),), requirements=((GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))),),), - assumptions=(), ) assert not t.contains_all_patterns_locally_for_crossing((1, 0)) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (0, 2)) t = Tiling( obstructions=(GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))),), requirements=((GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))),),), - assumptions=(), ) assert not t.contains_all_patterns_locally_for_crossing((1, 0)) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in (0, 2)) @@ -2742,7 +2724,6 @@ def test_contains_all_patterns_locally_for_crossing(): GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), ), ), - assumptions=(), ) assert all(t.contains_all_patterns_locally_for_crossing((i, 0)) for i in range(3)) diff --git a/tilings/algorithms/enumeration.py b/tilings/algorithms/enumeration.py index ca0d11f5..37c1459e 100644 --- a/tilings/algorithms/enumeration.py +++ b/tilings/algorithms/enumeration.py @@ -61,17 +61,12 @@ def __init__(self, tiling, no_req=False): self.no_req = no_req def verified(self) -> bool: + if self.tiling.parameters: + raise NotImplementedError if self.no_req and self.tiling.requirements: return False - return ( - all(gp.is_single_cell() for gp in self.tiling.obstructions) - and all(self._req_is_single_cell(req) for req in self.tiling.requirements) - and all( - gp.is_single_cell() - for gp in chain.from_iterable( - ass.gps for ass in self.tiling.assumptions - ) - ) + return all(gp.is_single_cell() for gp in self.tiling.obstructions) and all( + self._req_is_single_cell(req) for req in self.tiling.requirements ) @staticmethod @@ -101,12 +96,12 @@ def get_genf(self, **kwargs) -> Expr: avoided = self.tiling.__class__( self.tiling.obstructions + reqs, self.tiling.requirements[1:], - self.tiling.assumptions, + self.tiling.parameters, ) without = self.tiling.__class__( self.tiling.obstructions, self.tiling.requirements[1:], - self.tiling.assumptions, + self.tiling.parameters, ) avgf = LocalEnumeration(avoided).get_genf(funcs=funcs) wogf = LocalEnumeration(without).get_genf(funcs=funcs) diff --git a/tilings/algorithms/factor.py b/tilings/algorithms/factor.py index fafb635f..a02bf133 100644 --- a/tilings/algorithms/factor.py +++ b/tilings/algorithms/factor.py @@ -3,9 +3,9 @@ from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, Optional, Set, Tuple from permuta.misc import UnionFind -from tilings import GriddedPerm -from tilings.assumptions import TrackingAssumption +from tilings.griddedperm import GriddedPerm from tilings.misc import partitions_iterator +from tilings.parameter_counter import ParameterCounter if TYPE_CHECKING: from tilings import Tiling @@ -22,8 +22,7 @@ class Factor: 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. + Cell are also united according to the parameters on the tiling. """ def __init__(self, tiling: "Tiling") -> None: @@ -38,7 +37,7 @@ def __init__(self, tiling: "Tiling") -> None: Tuple[ Tuple[GriddedPerm, ...], Tuple[ReqList, ...], - Tuple[TrackingAssumption, ...], + Tuple[ParameterCounter, ...], ] ] ] = None @@ -72,17 +71,12 @@ def _unite_cells(self, cells: Iterable[Cell]) -> None: c2_int = self._cell_to_int(c2) self._cell_unionfind.unite(c1_int, c2_int) - def _unite_assumptions(self) -> None: + def _unite_parameters(self) -> None: """ - For each TrackingAssumption unite all the positions of the gridded perms. + Unite according to parameters. """ - for assumption in self._tiling.assumptions: - if isinstance(assumption, ComponentAssumption): - for comp in assumption.get_components(self._tiling): - self._unite_cells(chain.from_iterable(gp.pos for gp in comp)) - else: - for gp in assumption.gps: - self._unite_cells(gp.pos) + if self._tiling.parameters: + raise NotImplementedError def _unite_obstructions(self) -> None: """ @@ -126,7 +120,7 @@ def _unite_all(self) -> None: """ self._unite_obstructions() self._unite_requirements() - self._unite_assumptions() + self._unite_parameters() self._unite_rows_and_cols() def get_components(self) -> Tuple[Set[Cell], ...]: @@ -148,7 +142,7 @@ def _get_factors_obs_and_reqs( self, ) -> List[ Tuple[ - Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple[TrackingAssumption, ...] + Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple[ParameterCounter, ...] ], ]: """ @@ -167,16 +161,13 @@ def _get_factors_obs_and_reqs( 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 - ) + assert not self._tiling.parameters + parameters: Tuple[ParameterCounter, ...] = tuple() factors.append( ( obstructions, requirements, - tuple(set(ass for ass in assumptions if ass.gps)), + parameters, ) ) self._factors_obs_and_reqs = factors @@ -196,7 +187,7 @@ def factors(self) -> Tuple["Tiling", ...]: self._tiling.__class__( obstructions=f[0], requirements=f[1], - assumptions=tuple(sorted(f[2])), + parameters=tuple(sorted(f[2])), simplify=False, ) for f in self._get_factors_obs_and_reqs() @@ -216,12 +207,12 @@ def reducible_factorisations(self) -> Iterator[Tuple["Tiling", ...]]: for partition in partitions_iterator(min_comp): factors = [] for part in partition: - obstructions, requirements, assumptions = zip(*part) + obstructions, requirements, parameters = zip(*part) factors.append( self._tiling.__class__( obstructions=chain(*obstructions), requirements=chain(*requirements), - assumptions=tuple(sorted(chain(*assumptions))), + parameters=tuple(sorted(chain(*parameters))), simplify=False, ) ) @@ -276,6 +267,6 @@ def _unite_all(self) -> None: """ Unite all the cells that share an obstruction or a requirement list. """ - self._unite_assumptions() + self._unite_parameters() self._unite_obstructions() self._unite_requirements() diff --git a/tilings/algorithms/general_fusion.py b/tilings/algorithms/general_fusion.py index fe410f0f..2baf6362 100644 --- a/tilings/algorithms/general_fusion.py +++ b/tilings/algorithms/general_fusion.py @@ -1,11 +1,9 @@ -from collections import defaultdict -from itertools import chain, product -from typing import Counter, DefaultDict, Dict, Iterable, Iterator, List, Optional, Tuple +from itertools import chain +from typing import Counter, Dict, Iterator, List, Optional from tilings.algorithms.fusion import Fusion from tilings.assumptions import TrackingAssumption -from tilings.griddedperm import Cell, GriddedPerm -from tilings.misc import union_reduce +from tilings.griddedperm import GriddedPerm from tilings.tiling import Tiling diff --git a/tilings/algorithms/requirement_placement.py b/tilings/algorithms/requirement_placement.py index 56f427e4..6d1166f2 100644 --- a/tilings/algorithms/requirement_placement.py +++ b/tilings/algorithms/requirement_placement.py @@ -2,8 +2,8 @@ from typing import TYPE_CHECKING, Dict, FrozenSet, Iterable, List, Tuple from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS -from tilings import GriddedPerm -from tilings.assumptions import TrackingAssumption +from tilings.griddedperm import GriddedPerm +from tilings.parameter_counter import ParameterCounter if TYPE_CHECKING: from tilings import Tiling @@ -13,7 +13,7 @@ ListRequirement = List[GriddedPerm] ObsCache = Dict[Cell, List[GriddedPerm]] ReqsCache = Dict[Cell, List[ListRequirement]] -AssCache = Dict[Cell, List[TrackingAssumption]] +ParamCache = Dict[Cell, List[ParameterCounter]] class RequirementPlacement: @@ -52,7 +52,7 @@ def __init__( self.own_col = own_col self._stretched_obstructions_cache: ObsCache = {} self._stretched_requirements_cache: ReqsCache = {} - self._stretched_assumptions_cache: AssCache = {} + self._stretched_parameters_cache: ParamCache = {} if self.own_row and self.own_col: self.directions = frozenset(DIRS) elif self.own_row: @@ -244,31 +244,29 @@ def stretched_requirements(self, cell: Cell) -> List[ListRequirement]: ] return self._stretched_requirements_cache[cell] - def stretched_assumptions(self, cell: Cell) -> List[TrackingAssumption]: + def stretched_parameters(self, cell: Cell) -> List[ParameterCounter]: """ - Return all of the stretched assumptions that are created if placing a + Return all of the stretched parameters that are created if placing a point in the given cell. """ - if cell not in self._stretched_assumptions_cache: - self._stretched_assumptions_cache[cell] = [ - ass.__class__(self._stretch_gridded_perms(ass.gps, cell)) - for ass in self._tiling.assumptions - ] - return self._stretched_assumptions_cache[cell] + if self._tiling.parameters: + raise NotImplementedError + else: + return [] - def _stretched_obstructions_requirements_and_assumptions( + def _stretched_obstructions_requirements_and_parameters( self, cell: Cell - ) -> Tuple[List[GriddedPerm], List[ListRequirement], List[TrackingAssumption]]: + ) -> Tuple[List[GriddedPerm], List[ListRequirement], List[ParameterCounter]]: """ Return all of the stretched obstruction and requirements assuming that a point is placed in cell. """ stretched_obs = self.stretched_obstructions(cell) stretched_reqs = self.stretched_requirements(cell) - stretched_ass = self.stretched_assumptions(cell) + stretched_params = self.stretched_parameters(cell) point_obs = self._point_obstructions(cell) point_req = self._point_requirements(cell) - return stretched_obs + point_obs, stretched_reqs + point_req, stretched_ass + return stretched_obs + point_obs, stretched_reqs + point_req, stretched_params @staticmethod def _farther(c1: Cell, c2: Cell, direction: Dir) -> bool: @@ -352,8 +350,8 @@ def place_point_of_req( cells = frozenset(gp.pos[idx] for idx, gp in zip(indices, gps)) res = [] for cell in sorted(cells): - stretched = self._stretched_obstructions_requirements_and_assumptions(cell) - (obs, reqs, ass) = stretched + stretched = self._stretched_obstructions_requirements_and_parameters(cell) + (obs, reqs, param) = stretched forced_obs = self.forced_obstructions_from_requirement( gps, indices, cell, direction ) @@ -367,7 +365,7 @@ def place_point_of_req( self._tiling.__class__( new_obs, reqs + [rem_req], - assumptions=ass, + parameters=param, already_minimized_obs=True, ) ) diff --git a/tilings/algorithms/row_col_separation.py b/tilings/algorithms/row_col_separation.py index 8d4c92be..2e8d9f9c 100644 --- a/tilings/algorithms/row_col_separation.py +++ b/tilings/algorithms/row_col_separation.py @@ -11,8 +11,6 @@ from itertools import combinations, product from typing import TYPE_CHECKING, Dict, List, Tuple -from tilings import GriddedPerm - if TYPE_CHECKING: from tilings import Tiling @@ -392,9 +390,9 @@ def _separates_tiling(self, row_order, col_order): cell_map = self._get_cell_map(row_order, col_order) obs = self.map_obstructions(cell_map) reqs = self.map_requirements(cell_map) - ass = self.map_assumptions(cell_map) + params = self.map_parameters(cell_map) return self._tiling.__class__( - obstructions=obs, requirements=reqs, assumptions=ass + obstructions=obs, requirements=reqs, parameters=params ) @staticmethod @@ -428,15 +426,11 @@ def map_requirements(self, cell_map): for req_list in self._tiling.requirements: yield [self._map_gridded_perm(cell_map, req) for req in req_list] - def map_assumptions(self, cell_map): - """Map the assumptions of a tiling according to the cell map.""" - for ass in self._tiling.assumptions: - gps: List[GriddedPerm] = [] - for gp in ass.gps: - mapped_gp = self._map_gridded_perm(cell_map, gp) - if not mapped_gp.contradictory(): - gps.append(mapped_gp) - yield ass.__class__(gps) + def map_parameters(self, cell_map): + """Map the parameters of a tiling according to the cell map.""" + if self._tiling.parameters: + raise NotImplementedError + return [] @property def max_row_order(self): diff --git a/tilings/assumptions.py b/tilings/assumptions.py index 0ef25229..dab7f5ff 100644 --- a/tilings/assumptions.py +++ b/tilings/assumptions.py @@ -1,202 +1,4 @@ -import abc -from collections import defaultdict -from importlib import import_module -from itertools import chain, product -from typing import ( - TYPE_CHECKING, - Callable, - DefaultDict, - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Optional, - Set, - Tuple, - Type, -) - -from permuta import Perm - -from .griddedperm import GriddedPerm -from .map import RowColMap - -Cell = Tuple[int, int] - -if TYPE_CHECKING: - from tilings.tiling import Tiling - - -class GriddingsCounter: - """Counts the number of griddings that the subgridded perm of a gp using - the active cells has onto the tiling with respect to the mapping. - The active cells are the values of the cell map.""" - - def __init__(self, tiling: "Tiling", cell_map: Callable[[Cell], Cell]): - self.tiling = tiling - self.cell_map = cell_map - self._active_cells: Optional[FrozenSet[Cell]] = None - self.GP_CACHE: Dict[int, DefaultDict[GriddedPerm, Set[GriddedPerm]]] = {} - - @property - def active_cells(self) -> FrozenSet[Cell]: - if self._active_cells is None: - self._active_cells = frozenset( - self.cell_map(cell) for cell in self.tiling.active_cells - ) - return self._active_cells - - def _griddings(self, size: int) -> DefaultDict[GriddedPerm, Set[GriddedPerm]]: - if size not in self.GP_CACHE: - res: DefaultDict[GriddedPerm, Set[GriddedPerm]] = defaultdict(set) - for gp in self.tiling.objects_of_size(size): - mapped_gp = gp.apply_map(self.cell_map) - res[mapped_gp].add(gp) - self.GP_CACHE[size] = res - return self.GP_CACHE[size] - - def count_griddings(self, gp: GriddedPerm): - subgp = gp.get_gridded_perm_in_cells(self.active_cells) - return len(self._griddings(len(subgp))[subgp]) - - def preimage(self, gp: GriddedPerm) -> Set[GriddedPerm]: - return self._griddings(len(gp))[gp] - - class TrackingAssumption: """ - An assumption used to keep track of the griddings of a tiling. + Deprecated class. Use ParameterCounter. """ - - def __init__( - self, - tiling: "Tiling", - row_col_map: RowColMap, - ): - assert not tiling.assumptions - self.tiling = tiling - self.map = row_col_map - self.remove_empty_rows_and_cols() - self._init_checked() - self.gridding_counter = GriddingsCounter(self.tiling, self.map.map_cell) - self._ignore_reqs_gridding_counter = GriddingsCounter( - self.tiling.remove_requirements(), self.map.map_cell - ) - - def _init_checked(self): - """ - Ensure that intervals are preseved with respect to row and col maps. - """ - assert self.map.is_non_crossing() - - def remove_empty_rows_and_cols(self) -> None: - """ - Update the col and row maps after removing cols and rows that - became empty when tiling was created. - """ - self.map = self.tiling.backward_map.compose(self.map) - - def apply_row_col_map(self, row_col_map: "RowColMap") -> "TrackingAssumption": - """ - Modify in place the map with respect to the given row_col_map. Return self. - """ - self.map = self.map.compose(row_col_map) - return self - - def backward_map_gridded_perm( - self, gp: GriddedPerm, ignore_reqs: bool = False - ) -> Set[GriddedPerm]: - """Yield the gridded perms that map to gp according to the col and row maps.""" - if ignore_reqs: - return self._ignore_reqs_gridding_counter.preimage(gp) - return self.gridding_counter.preimage(gp) - - def forward_map_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: - """Map the gridded perm according to the col and row maps.""" - assert gp.avoids(*self.tiling.obstructions) and all( - gp.contains(*req) for req in self.tiling.requirements - ) - return self.map.map_gp(gp) - - def add_obstructions(self, obs: Tuple[GriddedPerm, ...]) -> "TrackingAssumption": - new_obs = chain.from_iterable( - self.backward_map_gridded_perm(gp, False) for gp in obs - ) - return TrackingAssumption(self.tiling.add_obstructions(new_obs), self.map) - - def add_obstructions_and_requirements( - self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]] - ) -> "TrackingAssumption": - new_obs = chain.from_iterable( - self.backward_map_gridded_perm(gp, False) for gp in obs - ) - new_reqs = chain.from_iterable( - chain.from_iterable(self.backward_map_gridded_perm(gp, False) for gp in req) - for req in reqs - ) - return TrackingAssumption( - self.tiling.add_obstructions_and_requirements(new_obs, new_reqs), self.map - ) - - def add_list_requirement( - self, req_list: Iterable[GriddedPerm] - ) -> "TrackingAssumption": - new_req = chain.from_iterable( - self.backward_map_gridded_perm(gp, False) for gp in req_list - ) - return TrackingAssumption(self.tiling.add_list_requirement(new_req), self.map) - - def is_identity(self): - raise NotImplementedError - - def get_value(self, gp: GriddedPerm) -> int: - """ - Return the number of griddings corresponding to gp. - """ - return self.gridding_counter.count_griddings(gp) - - def to_jsonable(self) -> dict: - """Return a dictionary form of the assumption.""" - raise NotImplementedError - - @classmethod - def from_dict(cls, d: dict) -> "TrackingAssumption": - """Return the assumption from the json dict representation.""" - raise NotImplementedError - - def __eq__(self, other: object) -> bool: - if not isinstance(other, TrackingAssumption): - return NotImplemented - return self.tiling == other.tiling and self.map == other.map - - def __lt__(self, other: object) -> bool: - if not isinstance(other, TrackingAssumption): - return NotImplemented - key_self = ( - self.__class__.__name__, - self.tiling.obstructions, - self.tiling.requirements, - self.map, - ) - key_other = ( - other.__class__.__name__, - other.tiling.obstructions, - other.tiling.requirements, - self.map, - ) - return key_self < key_other - - def __hash__(self) -> int: - return hash((self.tiling, self.map)) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.tiling!r}), {self.map!r})" - - def __str__(self): - map_str = " " + str(self.map).replace("\n", "\n ") - tiling_str = " " + str(self.tiling).replace("\n", "\n ") - return ( - "Counting the griddings with respect to the " - + f"map\n{map_str}\non the tiling:\n{tiling_str}" - ) diff --git a/tilings/map.py b/tilings/map.py index 2e6d4d71..cbd8a782 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -1,10 +1,9 @@ import itertools -from typing import TYPE_CHECKING, Dict, Optional, Tuple +from typing import TYPE_CHECKING, Dict, Iterator, Optional, Tuple from tilings.exception import InvalidOperationError if TYPE_CHECKING: - from tilings.assumptions import TrackingAssumption from tilings.griddedperm import GriddedPerm Cell = Tuple[int, int] @@ -84,6 +83,7 @@ def is_non_crossing(self) -> bool: rows = [b for _, b in sorted(self._row_map.items())] return cols == sorted(cols) and rows == sorted(rows) + # Mapping method def is_mappable_gp(self, gp: "GriddedPerm") -> bool: """ Return True if all the cell used by the gridded perm can be mapped. @@ -96,16 +96,6 @@ def map_gp(self, gp: "GriddedPerm") -> "GriddedPerm": """ return gp.__class__(gp.patt, map(self.map_cell, gp.pos)) - def map_assumption(self, assumption: "TrackingAssumption") -> "TrackingAssumption": - """ - Map the assumption according to the map. - - If some of the gridded permutation tracked by the assumption cannot be mapped - they are removed from the assumption. - """ - gps = tuple(self.map_gp(gp) for gp in assumption.gps if self.is_mappable_gp(gp)) - return assumption.__class__(gps) - def is_mappable_cell(self, cell: Cell) -> bool: """ Return True if the cell can be mapped, i.e. if the image of the row @@ -143,6 +133,32 @@ def map_col(self, col: int) -> int: """ return self._col_map[col] + # Pre-image method + def preimage_row(self, row: int) -> Iterator[int]: + """Returns all the preimages of the given row.""" + return (k for k, v in self._row_map.items() if v == row) + + def preimage_col(self, col: int) -> Iterator[int]: + """Returns all the preimages of the given column.""" + return (k for k, v in self._col_map.items() if v == col) + + def preimage_cell(self, cell: Cell) -> Iterator[Cell]: + """Returns all the preimages of the given cell.""" + col, row = cell + return itertools.product(self.preimage_col(col), self.preimage_row(row)) + + def preimage_gp(self, gp: "GriddedPerm") -> Iterator["GriddedPerm"]: + """ + Returns all the preimages of the given gridded permutation. + + Gridded permutations that are contradictory are filtered out. + """ + for pos in itertools.product(*(self.preimage_cell(cell) for cell in gp.pos)): + new_gp = gp.__class__(gp.patt, pos) + if not new_gp.contradictory(): + yield new_gp + + # Other method def max_row(self) -> int: """Return the biggest row index in the image.""" return max(self._row_map.values()) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py new file mode 100644 index 00000000..21ea7248 --- /dev/null +++ b/tilings/parameter_counter.py @@ -0,0 +1,179 @@ +import itertools +from typing import TYPE_CHECKING, Iterable, Iterator, Tuple + +from .griddedperm import GriddedPerm +from .map import RowColMap + +Cell = Tuple[int, int] + +if TYPE_CHECKING: + from tilings import Tiling + + +class PreimageCounter: + def __init__( + self, + tiling: "Tiling", + row_col_map: RowColMap, + ): + self.tiling = tiling + self.map = row_col_map + self.remove_empty_rows_and_cols() + self._init_checked() + + def _init_checked(self): + """ + Some sanity check on the counter. + """ + assert self.map.is_non_crossing() + assert not self.tiling.parameters + + def remove_empty_rows_and_cols(self) -> None: + """ + Update the col and row maps after removing cols and rows that + became empty when tiling was created. + """ + self.map = self.tiling.backward_map.compose(self.map) + + def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": + """ + Modify in place the map with respect to the given row_col_map. Return self. + """ + self.map = self.map.compose(row_col_map) + return self + + def num_preimage(self, gp: GriddedPerm) -> int: + """ + Return the number of preimage for the given gridded permutation. + """ + return sum(1 for _ in self.preimage(gp)) + + def preimage( + self, gp: GriddedPerm, ignore_reqs: bool = False + ) -> Iterator[GriddedPerm]: + """Return the preimage of the given gridded on the tiling.""" + # TODO: I think the bool is not needed anywhere + if ignore_reqs: + raise NotImplementedError("We needed it is the end") + return filter(self.tiling.__contains__, self.map.preimage_gp(gp)) + + def add_obstructions_and_requirements( + self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]] + ) -> "PreimageCounter": + """ + Add the given obstructions and requirements to the tiling. + """ + new_obs = itertools.chain.from_iterable(self.preimage(gp, False) for gp in obs) + new_reqs = ( + itertools.chain.from_iterable(self.preimage(gp, False) for gp in req) + for req in reqs + ) + return PreimageCounter( + self.tiling.add_obstructions_and_requirements(new_obs, new_reqs), self.map + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PreimageCounter): + return NotImplemented + return self.tiling == other.tiling and self.map == other.map + + def __lt__(self, other: object) -> bool: + if not isinstance(other, PreimageCounter): + return NotImplemented + key_self = ( + self.__class__.__name__, + self.tiling.obstructions, + self.tiling.requirements, + self.map, + ) + key_other = ( + other.__class__.__name__, + other.tiling.obstructions, + other.tiling.requirements, + self.map, + ) + return key_self < key_other + + def __hash__(self) -> int: + return hash((self.tiling, self.map)) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.tiling!r}), {self.map!r})" + + def __str__(self): + map_str = " " + str(self.map).replace("\n", "\n ") + tiling_str = " " + str(self.tiling).replace("\n", "\n ") + return ( + "Counting the griddings with respect to the " + + f"map\n{map_str}\non the tiling:\n{tiling_str}" + ) + + +class ParameterCounter: + """ + An aggregation of PreimageCounter. + """ + + def __init__(self, counters: Iterable[PreimageCounter]): + self.counters = tuple(sorted(set(counters))) + + def active_region(self) -> Iterator[Cell]: + """ + Return the region of the assumption considered active. + """ + raise NotImplementedError + + def get_value(self, gp: GriddedPerm) -> int: + """ + Return the value of the parameter for the given gridded permutation. + """ + return sum(counter.num_preimage(gp) for counter in self.counters) + + def to_jsonable(self) -> dict: + raise NotImplementedError + + @classmethod + def from_dict(cls, d: dict) -> "ParameterCounter": + raise NotImplementedError + + def apply_row_col_map(self, row_col_map: "RowColMap") -> "ParameterCounter": + """ + Modify in place the map with of each counter with respect to the given + row_col_map. Return self. + """ + for counter in self.counters: + counter.apply_row_col_map(row_col_map) + return self + + def add_obstructions_and_requirements( + self, obs: Iterable[GriddedPerm], reqs: Iterable[Iterable[GriddedPerm]] + ) -> "ParameterCounter": + """ + Add the given obstructions and requirement to all the tilings of the preimage + counters. + """ + return ParameterCounter( + ( + counter.add_obstructions_and_requirements(obs, reqs) + for counter in self.counters + ) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ParameterCounter): + return NotImplemented + return self.counters == other.counters + + def __lt__(self, other: object) -> bool: + if not isinstance(other, ParameterCounter): + return NotImplemented + return self.counters < other.counters + + def __hash__(self) -> int: + return hash(self.counters) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.counters!r})" + + def __str__(self): + return "".join(map(str, self.counters)) diff --git a/tilings/tilescope.py b/tilings/tilescope.py index ca1380fd..11036d92 100644 --- a/tilings/tilescope.py +++ b/tilings/tilescope.py @@ -124,7 +124,7 @@ def __init__( *args, **kwargs ): - self.tilings = frozenset(t.remove_assumptions() for t in tilings) + self.tilings = frozenset(t.remove_parameters() for t in tilings) super().__init__(basis, pack, *args, **kwargs) for t in self.tilings: class_label = self.classdb.get_label(t) @@ -139,7 +139,7 @@ def _expand( strategies: Tuple[CSSstrategy, ...], inferral: bool, ) -> None: - if comb_class.remove_assumptions() not in self.tilings: + if comb_class.remove_parameters() not in self.tilings: return return super()._expand(comb_class, label, strategies, inferral) diff --git a/tilings/tiling.py b/tilings/tiling.py index 089411ab..b2335ff5 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -44,12 +44,12 @@ SubobstructionInferral, guess_obstructions, ) -from .assumptions import TrackingAssumption from .exception import InvalidOperationError from .griddedperm import GriddedPerm from .gui_launcher import run_gui from .map import RowColMap from .misc import intersection_reduce, union_reduce +from .parameter_counter import ParameterCounter __all__ = ["Tiling"] @@ -92,7 +92,7 @@ def __init__( self, obstructions: Iterable[GriddedPerm] = tuple(), requirements: Iterable[Iterable[GriddedPerm]] = tuple(), - assumptions: Iterable[Iterable[TrackingAssumption]] = tuple(), + parameters: Iterable[ParameterCounter] = tuple(), remove_empty_rows_and_cols: bool = True, derive_empty: bool = True, simplify: bool = True, @@ -115,15 +115,15 @@ def __init__( self._obstructions = tuple(obstructions) # Set of requirement lists self._requirements = tuple(tuple(r) for r in requirements) - # Set of assumptions - self._assumptions = tuple(tuple(a) for a in assumptions) + # Set of parameters + self._parameters = tuple(parameters) else: # Set of obstructions self._obstructions = tuple(sorted(obstructions)) # Set of requirement lists self._requirements = Tiling.sort_requirements(requirements) - # Set of assumptions - self._assumptions = Tiling.sort_requirements(assumptions) + # Set of parameters + self._parameters = tuple(sorted(parameters)) # Simplify the set of obstructions and the set of requirement lists if simplify: @@ -142,7 +142,7 @@ def __init__( else: self._obstructions = (GriddedPerm.empty_perm(),) self._requirements = tuple() - self._assumptions = tuple() + self._parameters = tuple() self._cached_properties["active_cells"] = frozenset() self._cached_properties["backward_map"] = RowColMap.identity((0, 0)) self._cached_properties["cell_basis"] = {(0, 0): ([Perm()], [])} @@ -242,8 +242,8 @@ def _remove_empty_rows_and_cols(self) -> None: self._cached_properties["forward_map"] = RowColMap.identity((0, 0)) self._obstructions = (GriddedPerm.single_cell((0,), (0, 0)),) self._requirements = tuple() - assert not self._assumptions, "UH OH - we gotta think now" - self._assumptions = tuple() + assert not self._parameters, "UH OH - we gotta think now" + self._parameters = tuple() self._cached_properties["dimensions"] = (1, 1) return forward_map = self._minimize_mapping() @@ -260,11 +260,11 @@ def _remove_empty_rows_and_cols(self) -> None: tuple(forward_map.map_gp(req) for req in reqlist) for reqlist in self._requirements ) - self._assumptions = Tiling.sort_requirements( - [ - [ass.apply_row_col_map(forward_map) for ass in assumption] - for assumption in self.assumptions - ] + self._parameters = tuple( + sorted( + param_counter.apply_row_col_map(forward_map) + for param_counter in self._parameters + ) ) self._cached_properties["active_cells"] = frozenset( forward_map.map_cell(cell) @@ -315,7 +315,7 @@ def guess_from_gridded_perms( return cls( obstructions=guess_obstructions(gps, max_len), requirements=(), - assumptions=(), + parameters=(), ) def generate_known_equinumerous_tilings(self) -> Set["Tiling"]: @@ -394,23 +394,8 @@ def split_16bit(n) -> Tuple[int, int]: result.extend( chain.from_iterable([len(req)] + req.compress() for req in reqlist) ) - if self.assumptions: - result.extend(split_16bit(len(self.assumptions))) - for assumption in self.assumptions: - if isinstance(assumption, SkewComponentAssumption): - result.append(2) - elif isinstance(assumption, SumComponentAssumption): - result.append(1) - elif isinstance(assumption, TrackingAssumption): - result.append(0) - else: - raise ValueError("Not a valid assumption.") - result.extend(split_16bit(len(assumption.gps))) - result.extend( - chain.from_iterable( - [len(gp)] + gp.compress() for gp in assumption.gps - ) - ) + if self.parameters: + raise NotImplementedError res = array("B", result) return res.tobytes() @@ -453,29 +438,13 @@ def recreate_gp_list(offset): reqlist, offset = recreate_gp_list(offset) requirements.append(reqlist) - assumptions = [] + parameters: List[ParameterCounter] = [] if offset < len(arr): - nassumptions = merge_8bit(arr[offset], arr[offset + 1]) - offset += 2 - for _ in range(nassumptions): - assumption_type = arr[offset] - offset += 1 - gps, offset = recreate_gp_list(offset) - if assumption_type == 0: - # tracking - assumptions.append(TrackingAssumption(gps)) - elif assumption_type == 1: - # sum - assumptions.append(SumComponentAssumption(gps)) - elif assumption_type == 2: - # skew - assumptions.append(SkewComponentAssumption(gps)) - else: - raise ValueError("Invalid assumption type.") + raise NotImplementedError return cls( obstructions=obstructions, requirements=requirements, - assumptions=assumptions, + parameters=parameters, remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, @@ -503,7 +472,7 @@ def to_jsonable(self) -> dict: output["requirements"] = [ [gp.to_jsonable() for gp in req] for req in self.requirements ] - output["assumptions"] = [ass.to_jsonable() for ass in self.assumptions] + output["parameters"] = [param.to_jsonable() for param in self.parameters] return output @classmethod @@ -518,11 +487,11 @@ def from_dict(cls, d: dict) -> "Tiling": serialized Tiling object.""" obstructions = map(GriddedPerm.from_dict, d["obstructions"]) requirements = map(lambda x: map(GriddedPerm.from_dict, x), d["requirements"]) - assumptions = map(TrackingAssumption.from_dict, d.get("assumptions", [])) + parameters = map(ParameterCounter.from_dict, d.get("parameters", [])) return cls( obstructions=obstructions, requirements=requirements, - assumptions=assumptions, + parameters=parameters, ) # ------------------------------------------------------------- @@ -568,15 +537,8 @@ def add_obstructions( self, gps: Iterable[GriddedPerm], remove_empty_rows_and_cols: bool = True ) -> "Tiling": """Returns a new tiling with the obstructions added.""" - new_obs = tuple(gps) - return Tiling( - self._obstructions + new_obs, - self._requirements, - [ - [ass.add_obstructions(new_obs) for ass in assumption] - for assumption in self._assumptions - ], - remove_empty_rows_and_cols=remove_empty_rows_and_cols, + return self.add_obstructions_and_requirements( + gps, [], remove_empty_rows_and_cols ) def add_obstructions_and_requirements( @@ -592,11 +554,8 @@ def add_obstructions_and_requirements( self._obstructions + new_obs, self._requirements + new_reqs, [ - [ - ass.add_obstruction_and_requirements(new_obs, new_reqs) - for ass in assumption - ] - for assumption in self._assumptions + param_counter.add_obstructions_and_requirements(new_obs, new_reqs) + for param_counter in self._parameters ], remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) @@ -607,15 +566,8 @@ def add_list_requirement( """ Return a new tiling with the requirement list added. """ - new_req = tuple(req_list) - return Tiling( - self._obstructions, - self._requirements + (new_req,), - [ - [ass.add_list_requirement(new_req) for ass in assumption] - for assumption in self._assumptions - ], - remove_empty_rows_and_cols=remove_empty_rows_and_cols, + return self.add_obstructions_and_requirements( + [], [req_list], remove_empty_rows_and_cols ) def add_requirement( @@ -656,16 +608,16 @@ def add_single_cell_requirement( remove_empty_rows_and_cols=remove_empty_rows_and_cols, ) - def add_assumption(self, assumption: TrackingAssumption) -> "Tiling": - """Returns a new tiling with the added assumption.""" - return self.add_assumptions((assumption,)) + def add_parameter(self, parameter: ParameterCounter) -> "Tiling": + """Returns a new tiling with the added parameter.""" + return self.add_parameters((parameter,)) - def add_assumptions(self, assumptions: Iterable[TrackingAssumption]) -> "Tiling": - """Returns a new tiling with the added assumptions.""" + def add_parameters(self, parameters: Iterable[ParameterCounter]) -> "Tiling": + """Returns a new tiling with the added parameters.""" tiling = Tiling( self._obstructions, self._requirements, - self._assumptions + tuple(assumptions), + self._parameters + tuple(parameters), remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, @@ -673,19 +625,17 @@ def add_assumptions(self, assumptions: Iterable[TrackingAssumption]) -> "Tiling" ) return tiling - def remove_assumption(self, assumption: Iterable[TrackingAssumption]): - """Returns a new tiling with assumption removed.""" - assumption = tuple(sorted(set(assumption))) + def remove_parameter(self, parameter: Iterable[ParameterCounter]): + """Returns a new tiling with parameter removed.""" + parameter = tuple(sorted(set(parameter))) try: - idx = self._assumptions.index(assumption) + idx = self._parameters.index(parameter) except ValueError as e: - raise ValueError( - f"following assumption not on tiling: '{assumption}'" - ) from e + raise ValueError(f"following parameter not on tiling: '{parameter}'") from e tiling = Tiling( self._obstructions, self._requirements, - self._assumptions[:idx] + self._assumptions[idx + 1 :], + self._parameters[:idx] + self._parameters[idx + 1 :], remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, @@ -693,9 +643,9 @@ def remove_assumption(self, assumption: Iterable[TrackingAssumption]): ) return tiling - def remove_assumptions(self): + def remove_parameters(self): """ - Return the tiling with all assumptions removed. + Return the tiling with all parameters removed. """ return self.__class__( self._obstructions, @@ -710,7 +660,7 @@ def remove_requirements(self): """ Return the same tiling without the requirements. """ - assert not self.assumptions, "Now think" + assert not self.parameters, "Now think" return self.__class__( obstructions=self._obstructions, requirements=[], @@ -720,26 +670,6 @@ def remove_requirements(self): sorted_input=True, ) - def remove_components_from_assumptions(self): - """ - Return the tiling with all the actual components from individual - assumptions removed. - """ - if not self.assumptions: - return self - assumptions = [ass.remove_components(self) for ass in self.assumptions] - tiling = self.__class__( - self._obstructions, - self._requirements, - [ass for ass in assumptions if ass.gps], - remove_empty_rows_and_cols=False, - derive_empty=False, - simplify=False, - sorted_input=True, - ) - tiling.clean_assumptions() - return tiling - def fully_isolated(self) -> bool: """Check if all cells are isolated on their rows and columns.""" seen_row: List[int] = [] @@ -893,7 +823,6 @@ def skew_decomposition(self) -> List[List[Cell]]: def sort_requirements( requirements: Iterable[Iterable[GriddedPerm]], ) -> Tuple[Tuple[GriddedPerm, ...], ...]: - # TODO: fix name and typing to allow for assumptions return tuple(sorted(tuple(sorted(set(reqlist))) for reqlist in requirements)) @property @@ -926,15 +855,13 @@ def _transform( transformation of GriddedPerm that calls some internal method. # TODO: transf is not used... """ + if self._parameters: + raise NotImplementedError return Tiling( obstructions=(gptransf(ob) for ob in self.obstructions), requirements=( [gptransf(req) for req in reqlist] for reqlist in self.requirements ), - assumptions=( - ass.__class__(gptransf(gp) for gp in ass.gps) - for ass in self._assumptions - ), ) def reverse(self, regions=False): @@ -1104,7 +1031,7 @@ def sub_tiling( self, cells: Iterable[Cell], factors: bool = False, - add_assumptions: Iterable[TrackingAssumption] = tuple(), + add_parameters: Iterable[ParameterCounter] = tuple(), ) -> "Tiling": """Return the tiling using only the obstructions and requirements completely contained in the given cells. If factors is set to True, @@ -1121,19 +1048,11 @@ def sub_tiling( if (factors and req[0].pos[0] in cells) or all(c in cells for c in chain.from_iterable(r.pos for r in req)) ) - assumptions = tuple( - ass.__class__( - gp - for gp in ass.gps - if (factors and gp.pos[0] in cells) or all(c in cells for c in gp.pos) - ) - for ass in self.assumptions - ) + tuple(add_assumptions) - # TODO: check sum/skew assumptions + if self._parameters: + raise NotImplementedError return self.__class__( obstructions, requirements, - tuple(sorted(set(ass for ass in assumptions if ass.gps))), simplify=False, sorted_input=True, ) @@ -1300,8 +1219,8 @@ def is_subclass(self, perms_to_check: Iterable[Perm]): # HTML methods # ------------------------------------------------------------- - def _handle_html_assumption(self, result: List[str], style) -> List[str]: - """adds background color in cells where assumption happens""" + def _handle_html_parameter(self, result: List[str], style) -> List[str]: + """adds background color in cells where parameter happens""" # pylint: disable=too-many-locals colors = [ "#b0dbff", @@ -1315,39 +1234,35 @@ def _handle_html_assumption(self, result: List[str], style) -> List[str]: "#c8bdff", "#bfbfbf", ] - has_ass: Dict[int, List[str]] = {} - for c, ass in enumerate(self.assumptions): - for gp in ass.gps: - if len(gp.pos) > 1: + has_param: Dict[int, List[str]] = {} + for c, param_counter in enumerate(self.parameters): + for i, j in param_counter.active_region(): + dim_i, dim_j = self.dimensions + index = (dim_j - j - 1) * (3 * dim_i + 2) + i * 3 + 2 + if c >= len(colors): pass + elif index in has_param.keys(): + has_param[index].append(colors[c]) else: - i, j = gp.pos[0] - dim_i, dim_j = self.dimensions - index = (dim_j - j - 1) * (3 * dim_i + 2) + i * 3 + 2 - if c >= len(colors): - pass - elif index in has_ass.keys(): - has_ass[index].append(colors[c]) - else: - has_ass[index] = [colors[c]] - - if c >= len(colors) or len(has_ass[index]) > 4: - # display gray lines if out of color or - # more than 4 assumption in single cell - background_image = """background-image: - repeating-linear-gradient( - 45deg, #ffffff, #ffffff 6px, #00000080 1px, #00000080 7px - );""" - else: - # display stripes - background_image = "background-image: linear-gradient(180deg" - stripe_size = 24 // len(has_ass[index]) - for i, color in enumerate(has_ass[index]): - background_image += f""", - {color} {i*stripe_size}px, - {color} {(i+1)*stripe_size}px""" - background_image += ");" - result[index] = f'' + has_param[index] = [colors[c]] + + if c >= len(colors) or len(has_param[index]) > 4: + # display gray lines if out of color or + # more than 4 parameters in single cell + background_image = """background-image: + repeating-linear-gradient( + 45deg, #ffffff, #ffffff 6px, #00000080 1px, #00000080 7px + );""" + else: + # display stripes + background_image = "background-image: linear-gradient(180deg" + stripe_size = 24 // len(has_param[index]) + for i, color in enumerate(has_param[index]): + background_image += f""", + {color} {i*stripe_size}px, + {color} {(i+1)*stripe_size}px""" + background_image += ");" + result[index] = f'' return result def to_html_representation(self) -> str: @@ -1405,8 +1320,8 @@ def to_html_representation(self) -> str: index = row_index_from_top * row_width + cell[0] * 3 + 3 result[index] = label - # adds background color in cells where assumption happens - result = self._handle_html_assumption(result, style) + # adds background color in cells where parameter happens + result = self._handle_html_parameter(result, style) return "".join(result) # ------------------------------------------------------------- @@ -1415,48 +1330,33 @@ def to_html_representation(self) -> str: @property def extra_parameters(self) -> Tuple[str, ...]: - return tuple("k_{}".format(i) for i in range(len(self._assumptions))) + return tuple("k_{}".format(i) for i in range(len(self._parameters))) def get_parameters(self, obj: GriddedPerm) -> Parameters: - return tuple(ass.get_value(obj) for ass in self.assumptions) - - def possible_parameters(self, n: int) -> Iterator[Dict[str, int]]: - if any( - len(gp) > 1 - for gp in chain.from_iterable(ass.gps for ass in self.assumptions) - ): - raise NotImplementedError( - "possible parameters only implemented for assumptions with " - "size one gridded perms" - ) - parameters = [self.get_assumption_parameter(ass) for ass in self.assumptions] - for values in product(*[range(n + 1) for _ in parameters]): - yield dict(zip(parameters, values)) + return tuple(param.get_value(obj) for param in self.parameters) - def get_assumption_parameter(self, assumption: TrackingAssumption) -> str: + def get_parameter_name(self, parameter: ParameterCounter) -> str: """ - Return the variable associated with the given assumption. + Return the variable associated with the given parameter. - Raise ValueError if the assumptions is not on the tiling. + Raise ValueError if the parameter is not on the tiling. """ try: - idx = self._assumptions.index(assumption) + idx = self._parameters.index(parameter) except ValueError as e: - raise ValueError( - f"following assumption not on tiling: '{assumption}'" - ) from e + raise ValueError(f"following parameter not on tiling: '{parameter}'") from e return "k_{}".format(idx) - def get_assumption(self, parameter: str) -> TrackingAssumption: + def get_parameter(self, parameter: str) -> ParameterCounter: idx = parameter.split("_")[1] - return self.assumptions[int(idx)] + return self.parameters[int(idx)] - def get_minimum_value(self, parameter: str) -> int: + def get_minimum_value(self, param_name: str) -> int: """ Return the minimum value that can be taken by the parameter. """ - assumption = self.get_assumption(parameter) - return min(assumption.get_value(gp) for gp in self.minimal_gridded_perms()) + parameter = self.get_parameter(param_name) + return min(parameter.get_value(gp) for gp in self.minimal_gridded_perms()) def maximum_length_of_minimum_gridded_perm(self) -> int: """Returns the maximum length of the minimum gridded permutation that @@ -1495,7 +1395,7 @@ def is_finite(self) -> bool: def objects_of_size(self, n: int, **parameters: int) -> Iterator[GriddedPerm]: for gp in self.gridded_perms_of_length(n): if all( - self.get_assumption(k).get_value(gp) == val + self.get_parameter(k).get_value(gp) == val for k, val in parameters.items() ): yield gp @@ -1512,13 +1412,13 @@ def initial_conditions(self, check: int = 6) -> List[sympy.Expr]: """ res = [0 for _ in range(check + 1)] extra_params = self.extra_parameters - ass_counter = [ - (sympy.var(k), self.get_assumption(k).get_value) for k in extra_params + param_counter = [ + (sympy.var(k), self.get_parameter(k).get_value) for k in extra_params ] for gp in self.gridded_perms(check): res[len(gp)] += reduce( mul, - (var ** func(gp) for var, func in ass_counter), + (var ** func(gp) for var, func in param_counter), sympy.Number(1), ) return res @@ -1551,7 +1451,7 @@ def merge(self) -> "Tiling": requirements = tuple( GriddedPerm(gp.patt, gp.pos) for gp in mgps.minimal_gridded_perms() ) - return self.__class__(self.obstructions, (requirements,), self.assumptions) + return self.__class__(self.obstructions, (requirements,), self.parameters) def minimal_gridded_perms(self) -> Iterator[GriddedPerm]: """ @@ -1723,11 +1623,11 @@ def total_requirements(self) -> int: return len(self._requirements) @property - def assumptions(self) -> Tuple[Tuple[TrackingAssumption, ...], ...]: - return self._assumptions + def parameters(self) -> Tuple[ParameterCounter, ...]: + return self._parameters - def total_assumptions(self) -> int: - return len(self._assumptions) + def total_parameters(self) -> int: + return len(self._parameters) @property def empty_cells(self) -> CellFrozenSet: @@ -1812,7 +1712,7 @@ def rec( return Tiling( obstructions=list(self.obstructions) + res, requirements=self.requirements, - assumptions=self.assumptions, + parameters=self.parameters, ) @classmethod @@ -1867,27 +1767,16 @@ def to_gui(self): def __hash__(self) -> int: return ( - hash(self._requirements) - ^ hash(self._obstructions) - ^ hash(self._assumptions) + hash(self._requirements) ^ hash(self._obstructions) ^ hash(self._parameters) ) def __eq__(self, other: object) -> bool: if not isinstance(other, Tiling): - return False + return NotImplemented return ( self.obstructions == other.obstructions and self.requirements == other.requirements - and self.assumptions == other.assumptions - ) - - def __ne__(self, other: object) -> bool: - if not isinstance(other, Tiling): - return True - return ( - self.obstructions != other.obstructions - or self.requirements != other.requirements - or self.assumptions != other.assumptions + and self.parameters == other.parameters ) def __contains__(self, gp: GriddedPerm) -> bool: @@ -1897,12 +1786,12 @@ def __contains__(self, gp: GriddedPerm) -> bool: ) def __repr__(self) -> str: - format_string = "Tiling(obstructions={}, requirements={}, assumptions={})" + format_string = "Tiling(obstructions={}, requirements={}, parameters={})" non_point_obstructions = tuple( filterfalse(GriddedPerm.is_point_perm, self.obstructions) ) return format_string.format( - non_point_obstructions, self.requirements, self.assumptions + non_point_obstructions, self.requirements, self.parameters ) def __str__(self) -> str: @@ -1988,11 +1877,11 @@ def __str__(self) -> str: for r in req: result.append(str(r)) result.append("\n") - for i, ass in enumerate(self.assumptions): - result.append("Assumption {}:\n".format(str(i))) - result.extend(map(str, ass)) + for i, param in enumerate(self.parameters): + result.append("Parameter {}:\n".format(str(i))) + result.append(str(param)) result.append("\n") - if self.assumptions or self.requirements: + if self.parameters or self.requirements: result = result[:-1] return "".join(result) diff --git a/tox.ini b/tox.ini index 6ad38751..01b42b59 100644 --- a/tox.ini +++ b/tox.ini @@ -25,8 +25,9 @@ deps = commands = pytest [pytest] -addopts = --doctest-modules --doctest-ignore-import-errors -testpaths = tests tilings README.rst +; Should remove the ignore and add the readme to test path before merging into develop. +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore-glob=tests/strategies/ +testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE @@ -40,6 +41,13 @@ deps = commands = flake8 --isort-show-traceback tilings tests setup.py +; Extra ignore added. Should be removed before going into develop +[flake8] +per-file-ignores = + tilings/strategies/verification.py:F821 + tilings/algorithms/fusion.py:F821 + tilings/strategies/assumption_splitting.py:F821 + [testenv:pylint] description = run pylint (static code analysis) basepython = {[default]basepython} From 5c0b126b7f4be7ca5e0970772c0cdb2848a2b924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Wed, 29 Sep 2021 07:35:48 -0400 Subject: [PATCH 07/41] Fix requirement insertion on preimage counter (#358) * test for obs and req insertion on tiling with preimage * fix bug with req insertion on preimage --- tests/test_tiling_with_preimage.py | 124 +++++++++++++++++++++++++++++ tilings/parameter_counter.py | 13 +-- 2 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 tests/test_tiling_with_preimage.py diff --git a/tests/test_tiling_with_preimage.py b/tests/test_tiling_with_preimage.py new file mode 100644 index 00000000..55bb24d6 --- /dev/null +++ b/tests/test_tiling_with_preimage.py @@ -0,0 +1,124 @@ +import pytest + +from tilings import GriddedPerm, Tiling +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter + + +@pytest.fixture +def normal_fusion_tiling(): + preimage_t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + ) + preimage_map = RowColMap(col_map={0: 0, 1: 0, 2: 1}, row_map={0: 0}) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + + return Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + ), + parameters=[param], + ) + + +def test_insert_point_obs(normal_fusion_tiling): + # Insert in cell with one preimage + preimage_t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + ), + ) + preimage_map = RowColMap(col_map={0: 0, 1: 0}, row_map={0: 0}) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + assert normal_fusion_tiling.empty_cell((1, 0)) == Tiling( + [GriddedPerm.single_cell((0, 1), (0, 0))], parameters=[param] + ) + + # Insert in cell with two preimages + preimage_t = Tiling.from_string("123") + preimage_map = RowColMap.identity((1, 1)) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + assert normal_fusion_tiling.empty_cell((0, 0)) == Tiling( + [GriddedPerm.single_cell((0, 1, 2), (0, 0))], parameters=[param] + ) + + +def test_insert_point_req(normal_fusion_tiling): + # Insert in cell with one preimage + preimage_t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + requirements=[[GriddedPerm((0,), ((2, 0),))]], + ) + preimage_map = RowColMap(col_map={0: 0, 1: 0, 2: 1}, row_map={0: 0}) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + t = ( + normal_fusion_tiling.remove_parameters() + .insert_cell((1, 0)) + .add_parameter(param) + ) + assert t == normal_fusion_tiling.insert_cell((1, 0)) + # Insert in cell with two preimages + preimage_t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + requirements=[[GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))]], + ) + preimage_map = RowColMap(col_map={0: 0, 1: 0, 2: 1}, row_map={0: 0}) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + t = ( + normal_fusion_tiling.remove_parameters() + .insert_cell((0, 0)) + .add_parameter(param) + ) + assert t == normal_fusion_tiling.insert_cell((0, 0)) + + +def test_insert_point_req_tiling_with_req(normal_fusion_tiling): + # insert in cell with one preimage + t_base = normal_fusion_tiling.insert_cell((0, 0)) + preimage_t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + requirements=[ + [GriddedPerm((0,), ((2, 0),))], + [GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))], + ], + ) + preimage_map = RowColMap(col_map={0: 0, 1: 0, 2: 1}, row_map={0: 0}) + param = ParameterCounter([PreimageCounter(preimage_t, preimage_map)]) + t_expected = t_base.remove_parameters().insert_cell((1, 0)).add_parameter(param) + assert t_expected == t_base.insert_cell((1, 0)) + + # insert in cell with two preimages + t_base = normal_fusion_tiling.insert_cell((1, 0)) + assert t_expected == t_base.insert_cell((0, 0)) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 21ea7248..40435f7b 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -48,13 +48,8 @@ def num_preimage(self, gp: GriddedPerm) -> int: """ return sum(1 for _ in self.preimage(gp)) - def preimage( - self, gp: GriddedPerm, ignore_reqs: bool = False - ) -> Iterator[GriddedPerm]: - """Return the preimage of the given gridded on the tiling.""" - # TODO: I think the bool is not needed anywhere - if ignore_reqs: - raise NotImplementedError("We needed it is the end") + def preimage(self, gp: GriddedPerm) -> Iterator[GriddedPerm]: + """Return the preimage of the given gridded permutation on the tiling.""" return filter(self.tiling.__contains__, self.map.preimage_gp(gp)) def add_obstructions_and_requirements( @@ -63,9 +58,9 @@ def add_obstructions_and_requirements( """ Add the given obstructions and requirements to the tiling. """ - new_obs = itertools.chain.from_iterable(self.preimage(gp, False) for gp in obs) + new_obs = itertools.chain.from_iterable(self.map.preimage_gp(gp) for gp in obs) new_reqs = ( - itertools.chain.from_iterable(self.preimage(gp, False) for gp in req) + itertools.chain.from_iterable(self.map.preimage_gp(gp) for gp in req) for req in reqs ) return PreimageCounter( From 8d0a5c5e7611e34ba1944f6ede44ceaf5c2191a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Wed, 6 Oct 2021 05:14:15 -0400 Subject: [PATCH 08/41] Row columns separation strategy (#360) * typing rcs algo * introducing the cell map class * use the CellMap class in row col sep algorithm * add map_tiling method * allow crossing map for preimage counter * map preimage counter * make param counter iterable * implement map parameters * map inverse method and to_row_col_map - reverse -> inverse - implement inverse for cell map - implement conversion to row_col_map for CellMap * simplify projection computation * move param mapping stuff to the map class * fix the strategy * small cleaning * filter contradictory ob when mapping tiling * non map point obs when mapping tiling * cleaner map tiling method * Only mapping requirements that make sense --- mypy.ini | 3 + tests/algorithms/test_row_col_separation.py | 128 +++++++------ tilings/algorithms/row_col_separation.py | 186 ++++++++---------- tilings/map.py | 190 +++++++++++++++++-- tilings/parameter_counter.py | 4 +- tilings/strategies/row_and_col_separation.py | 34 ++-- tilings/tiling.py | 6 +- 7 files changed, 339 insertions(+), 212 deletions(-) diff --git a/mypy.ini b/mypy.ini index 8831bb1e..6d6c48d6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -25,3 +25,6 @@ ignore_errors = True [mypy-tilings.bijections] ignore_errors = True + +[mypy-tilings.strategies.row_and_col_separation] +ignore_errors = False diff --git a/tests/algorithms/test_row_col_separation.py b/tests/algorithms/test_row_col_separation.py index 08d7460b..9248b6d4 100644 --- a/tests/algorithms/test_row_col_separation.py +++ b/tests/algorithms/test_row_col_separation.py @@ -8,6 +8,7 @@ RowColSeparation, _RowColSeparationSingleApplication, ) +from tilings.map import CellMap # ---------------------------------------------------------------------------- # Test for the Graph class @@ -553,19 +554,6 @@ def test_separates_tiling(): ) -def test_map_gridded_perm(separable_tiling1): - rcs = _RowColSeparationSingleApplication(separable_tiling1) - ob = GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))) - cell_map = {(0, 0): (0, 0), (1, 0): (1, 1)} - assert rcs._map_gridded_perm(cell_map, ob) == GriddedPerm( - (0, 1, 2), ((0, 0), (1, 1), (1, 1)) - ) - ob = GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))) - assert rcs._map_gridded_perm(cell_map, ob) == GriddedPerm( - (0, 1, 2), ((0, 0), (1, 1), (1, 1)) - ) - - def test_all_separation(): t = Tiling( obstructions=[ @@ -897,24 +885,30 @@ def test_backmap(): ), requirements=((GriddedPerm((0,), ((1, 2),)),),), ) - cellmap1 = { - (0, 1): (0, 1), - (1, 0): (2, 0), - (1, 1): (2, 1), - (1, 2): (1, 2), - } - cellmap2 = { - (0, 1): (0, 1), - (1, 2): (1, 3), - (2, 0): (2, 0), - (2, 1): (3, 2), - } - final_cell_map = { - (0, 1): (0, 1), - (1, 0): (2, 0), - (1, 1): (3, 2), - (1, 2): (1, 3), - } + cellmap1 = CellMap( + { + (0, 1): (0, 1), + (1, 0): (2, 0), + (1, 1): (2, 1), + (1, 2): (1, 2), + } + ) + cellmap2 = CellMap( + { + (0, 1): (0, 1), + (1, 2): (1, 3), + (2, 0): (2, 0), + (2, 1): (3, 2), + } + ) + final_cell_map = CellMap( + { + (0, 1): (0, 1), + (1, 0): (2, 0), + (1, 1): (3, 2), + (1, 2): (1, 3), + } + ) rcs1 = _RowColSeparationSingleApplication(t) t1 = rcs1.separated_tiling() assert rcs1.get_cell_map() == cellmap1 @@ -959,30 +953,36 @@ def test_backmap2(): ), requirements=((GriddedPerm((0,), ((1, 0),)),),), ) - cellmap1 = { - (0, 0): (0, 0), - (0, 2): (0, 3), - (1, 0): (1, 1), - (1, 1): (2, 2), - (1, 2): (2, 3), - (2, 2): (3, 3), - } - cellmap2 = { - (0, 0): (0, 0), - (0, 3): (0, 3), - (1, 1): (1, 1), - (2, 2): (3, 2), - (2, 3): (2, 4), - (3, 3): (4, 3), - } - final_cell_map = { - (0, 0): (0, 0), - (0, 2): (0, 3), - (1, 0): (1, 1), - (1, 1): (3, 2), - (1, 2): (2, 4), - (2, 2): (4, 3), - } + cellmap1 = CellMap( + { + (0, 0): (0, 0), + (0, 2): (0, 3), + (1, 0): (1, 1), + (1, 1): (2, 2), + (1, 2): (2, 3), + (2, 2): (3, 3), + } + ) + cellmap2 = CellMap( + { + (0, 0): (0, 0), + (0, 3): (0, 3), + (1, 1): (1, 1), + (2, 2): (3, 2), + (2, 3): (2, 4), + (3, 3): (4, 3), + } + ) + final_cell_map = CellMap( + { + (0, 0): (0, 0), + (0, 2): (0, 3), + (1, 0): (1, 1), + (1, 1): (3, 2), + (1, 2): (2, 4), + (2, 2): (4, 3), + } + ) print(t) rcs1 = _RowColSeparationSingleApplication(t) t1 = rcs1.separated_tiling() @@ -1040,14 +1040,16 @@ def test_backmap3(): ), requirements=((GriddedPerm((0,), ((2, 1),)),),), ) - cellmap1 = { - (0, 0): (2, 0), - (0, 1): (0, 2), - (0, 2): (1, 4), - (2, 0): (3, 1), - (2, 1): (3, 3), - (2, 2): (3, 4), - } + cellmap1 = CellMap( + { + (0, 0): (2, 0), + (0, 1): (0, 2), + (0, 2): (1, 4), + (2, 0): (3, 1), + (2, 1): (3, 3), + (2, 2): (3, 4), + } + ) print(t) rcs1 = _RowColSeparationSingleApplication(t) t1 = rcs1.separated_tiling() diff --git a/tilings/algorithms/row_col_separation.py b/tilings/algorithms/row_col_separation.py index 2e8d9f9c..6a9d27a0 100644 --- a/tilings/algorithms/row_col_separation.py +++ b/tilings/algorithms/row_col_separation.py @@ -9,15 +9,31 @@ """ import heapq from itertools import combinations, product -from typing import TYPE_CHECKING, Dict, List, Tuple +from typing import ( + TYPE_CHECKING, + Generic, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) + +from tilings.map import CellMap if TYPE_CHECKING: - from tilings import Tiling + from tilings import GriddedPerm, Tiling Cell = Tuple[int, int] +Edge = Tuple[int, int] +Matrix = List[List[int]] +T = TypeVar("T") -class Graph: +class Graph(Generic[T]): """ A weighted directed graph implemented with an adjacency matrix. @@ -40,7 +56,7 @@ class Graph: - For the vertex order implied by a reduced acyclic graph """ - def __init__(self, vertices, matrix=None): + def __init__(self, vertices: Iterable[T], matrix: Matrix): self._vertex_labels = [set([v]) for v in vertices] self._vertex_weights = [1 for _ in self._vertex_labels] self._matrix = matrix @@ -50,13 +66,13 @@ def __init__(self, vertices, matrix=None): self._is_acyclic = False @property - def num_vertices(self): + def num_vertices(self) -> int: """ The number of vertices of the graph """ return len(self._vertex_weights) - def _merge_vertices(self, v1, v2): + def _merge_vertices(self, v1: int, v2: int) -> None: """ Merge the two vertices. @@ -71,7 +87,7 @@ def _merge_vertices(self, v1, v2): self._add_matrix_columns(v1, v2) self._trim_edges(v1) - def reduce(self): + def reduce(self) -> None: if self._reduced: return non_edge = self.find_non_edge() @@ -80,7 +96,7 @@ def reduce(self): non_edge = self.find_non_edge() self._reduced = True - def find_non_edge(self): + def find_non_edge(self) -> Tuple[int, int]: """ Return a non-edge of the graph. @@ -91,7 +107,7 @@ def find_non_edge(self): if not self._is_edge(v1, v2) and not self._is_edge(v2, v1): return (v1, v2) - def is_acyclic(self): + def is_acyclic(self) -> bool: """ Check if the graph is acyclic. @@ -103,7 +119,7 @@ def is_acyclic(self): return True return self.find_cycle() is None - def find_cycle(self): + def find_cycle(self) -> Optional[Union[Tuple[Edge, Edge], Tuple[Edge, Edge, Edge]]]: """ Return the edges of a cycle of the graphs. The graphs first need to be reduced @@ -129,7 +145,7 @@ def find_cycle(self): self._is_acyclic = True return None - def break_cycle_in_all_ways(self, edges): + def break_cycle_in_all_ways(self, edges: Iterable[Edge]) -> Iterator["Graph"]: """ Generator over Graph object obtained by removing one edge of the `edges` iterator. @@ -145,7 +161,7 @@ def break_cycle_in_all_ways(self, edges): new_graph._is_acyclic = False yield new_graph - def vertex_order(self): + def vertex_order(self) -> List[Set[T]]: """ Return the order of the vertex in a reduced acyclic graph. @@ -159,7 +175,7 @@ def vertex_order(self): vert_num_parent = [row.count(0) for row in self._matrix] return [p[1] for p in sorted(zip(vert_num_parent, self._vertex_labels))] - def _add_matrix_rows(self, row1_idx, row2_idx): + def _add_matrix_rows(self, row1_idx: int, row2_idx: int) -> None: """ Deletes row 2 from the graph matrix and change row 1 to the sum of both row. @@ -169,7 +185,7 @@ def _add_matrix_rows(self, row1_idx, row2_idx): row2 = self._matrix.pop(row2_idx) self._matrix[row1_idx] = list(map(sum, zip(row1, row2))) - def _add_matrix_columns(self, col1_idx, col2_idx): + def _add_matrix_columns(self, col1_idx: int, col2_idx: int) -> None: """ Deletes column 2 from the graph matrix and change column 1 to the sum of both column. @@ -179,7 +195,7 @@ def _add_matrix_columns(self, col1_idx, col2_idx): c2_value = row.pop(col2_idx) row[col1_idx] += c2_value - def _trim_edges(self, vertex): + def _trim_edges(self, vertex: int) -> None: """ Remove all the edges that touch vertex that that have a weight which is too small. @@ -195,7 +211,7 @@ def _trim_edges(self, vertex): self._delete_edge_if_small(v1, v2, weight_prod) self._delete_edge_if_small(v2, v1, weight_prod) - def _delete_edge_if_small(self, head, tail, cap): + def _delete_edge_if_small(self, head: int, tail: int, cap: int) -> None: """ Delete the edges that goes from head to tail if its weight is lower than the cap. @@ -204,10 +220,10 @@ def _delete_edge_if_small(self, head, tail, cap): if weight < cap: self._matrix[head][tail] = 0 - def _is_edge(self, v1, v2): + def _is_edge(self, v1: int, v2: int) -> bool: return self._matrix[v1][v2] != 0 - def _length3_cycle(self, v1, v2, v3): + def _length3_cycle(self, v1: int, v2: int, v3: int) -> Tuple[Edge, Edge, Edge]: """ Return the edges of a length 3 cycle containing the three vertices if such a cycle exist. Otherwise return None @@ -223,25 +239,29 @@ def is_cycle(edges): if is_cycle(orientation2): return orientation2 - def __repr__(self): + def __repr__(self) -> str: s = "Graph over the vertices {}\n".format(self._vertex_labels) s += "Vertex weight is {}\n".format(self._vertex_weights) for row in self._matrix: s += "{}\n".format(row) return s - def __lt__(self, other): + def __lt__(self, other: object) -> bool: """ A graph is 'smaller if it as more vertices. Useful for the priority queue """ + if not isinstance(other, Graph): + return NotImplemented return self.num_vertices > other.num_vertices - def __le__(self, other): + def __le__(self, other: object) -> bool: """ A graph is 'smaller if it as more vertices. Useful for the priority queue """ + if not isinstance(other, Graph): + return NotImplemented return self.num_vertices >= other.num_vertices @@ -250,22 +270,22 @@ class _RowColSeparationSingleApplication: Make the row separation of the tiling. """ - def __init__(self, tiling): + def __init__(self, tiling: "Tiling"): self._tiling = tiling self._active_cells = tuple(sorted(tiling.active_cells)) - self._ineq_matrices = None - self._max_row_order = None - self._max_col_order = None + self._ineq_matrices: Optional[Tuple[Matrix, Matrix]] = None + self._max_row_order: Optional[List[Set[Cell]]] = None + self._max_col_order: Optional[List[Set[Cell]]] = None - def cell_at_idx(self, idx): + def cell_at_idx(self, idx: int) -> Cell: """Return the cell at index `idx`.""" return self._active_cells[idx] - def cell_idx(self, cell): + def cell_idx(self, cell: Cell) -> int: """Return the index of the cell""" return self._active_cells.index(cell) - def _basic_matrix(self, row): + def _basic_matrix(self, row: bool) -> Matrix: """ Compute the basic matrix of inequalities based only on difference in row and columns. If `row` is True return the matrix for the row, @@ -274,12 +294,12 @@ def _basic_matrix(self, row): idx = 1 if row else 0 m = [] for c1 in self._active_cells: - row = [1 if c1[idx] < c2[idx] else 0 for c2 in self._active_cells] - m.append(row) + new_row = [1 if c1[idx] < c2[idx] else 0 for c2 in self._active_cells] + m.append(new_row) return m @staticmethod - def _row_cell_order(ob): + def _row_cell_order(ob: "GriddedPerm") -> Tuple[Cell, Cell]: """ Return the order of the two cells of a length 2 obstruction localized in a row. @@ -300,7 +320,7 @@ def _row_cell_order(ob): return c1, c2 @staticmethod - def _col_cell_order(ob): + def _col_cell_order(ob: "GriddedPerm") -> Tuple[Cell, Cell]: """ Return the order of the two cells of a length 2 obstruction. @@ -316,7 +336,7 @@ def _col_cell_order(ob): assert not c1[1] == c2[1], "Obstruction is single cell" return c2, c1 - def _add_ineq(self, ineq, matrix): + def _add_ineq(self, ineq: Tuple[Cell, Cell], matrix: Matrix) -> None: """ Add an inequalities to the matrix. @@ -325,7 +345,7 @@ def _add_ineq(self, ineq, matrix): small_c, big_c = ineq matrix[self.cell_idx(small_c)][self.cell_idx(big_c)] = 1 - def _complete_ineq_matrices(self): + def _complete_ineq_matrices(self) -> Tuple[Matrix, Matrix]: """ Return the matrices of inequalities between the cells. @@ -352,14 +372,14 @@ def _complete_ineq_matrices(self): self._ineq_matrices = row_m, col_m return self._ineq_matrices - def row_ineq_graph(self): + def row_ineq_graph(self) -> Graph: return Graph(self._active_cells, self._complete_ineq_matrices()[0]) - def col_ineq_graph(self): + def col_ineq_graph(self) -> Graph: return Graph(self._active_cells, self._complete_ineq_matrices()[1]) @staticmethod - def _all_order(graph, only_max=False): + def _all_order(graph: Graph, only_max: bool = False) -> Iterator[List[Set[Cell]]]: """ Generator of ordering of the active cells. @@ -382,21 +402,20 @@ def _all_order(graph, only_max=False): heapq.heappush(heap, g) @staticmethod - def _maximal_order(graph): + def _maximal_order(graph: Graph) -> List[Set[Cell]]: """Returns a order that maximise separation.""" return next(_RowColSeparationSingleApplication._all_order(graph)) - def _separates_tiling(self, row_order, col_order): + def _separates_tiling( + self, row_order: List[Set[Cell]], col_order: List[Set[Cell]] + ) -> "Tiling": cell_map = self._get_cell_map(row_order, col_order) - obs = self.map_obstructions(cell_map) - reqs = self.map_requirements(cell_map) - params = self.map_parameters(cell_map) - return self._tiling.__class__( - obstructions=obs, requirements=reqs, parameters=params - ) + return cell_map.map_tiling(self._tiling) @staticmethod - def _get_cell_map(row_order, col_order): + def _get_cell_map( + row_order: List[Set[Cell]], col_order: List[Set[Cell]] + ) -> CellMap: """ Return the position of the according to the given row_order and col_order. @@ -404,36 +423,18 @@ def _get_cell_map(row_order, col_order): This method does not account for any cleaning occuring in the initializer. For the complete cell map use `get_cell_map`. """ - cell_map = dict() + row_cell_map = dict() for i, row in enumerate(row_order): for cell in row: - cell_map[cell] = (None, i) + row_cell_map[cell] = i + cell_map = dict() for i, col in enumerate(col_order): for cell in col: - cell_map[cell] = (i, cell_map[cell][1]) - return cell_map - - def map_obstructions(self, cell_map): - """Map the obstruction of a tiling according to the cell map.""" - non_point_obs = (ob for ob in self._tiling.obstructions if len(ob) > 1) - for ob in non_point_obs: - ob = self._map_gridded_perm(cell_map, ob) - if not ob.contradictory(): - yield ob - - def map_requirements(self, cell_map): - """Map the requirements of a tiling according to the cell map.""" - for req_list in self._tiling.requirements: - yield [self._map_gridded_perm(cell_map, req) for req in req_list] - - def map_parameters(self, cell_map): - """Map the parameters of a tiling according to the cell map.""" - if self._tiling.parameters: - raise NotImplementedError - return [] + cell_map[cell] = (i, row_cell_map[cell]) + return CellMap(cell_map) @property - def max_row_order(self): + def max_row_order(self) -> List[Set[Cell]]: """A maximal order on the rows.""" if self._max_row_order is not None: return self._max_row_order @@ -441,24 +442,14 @@ def max_row_order(self): return self._max_row_order @property - def max_col_order(self): + def max_col_order(self) -> List[Set[Cell]]: """A maximal order on the columns.""" if self._max_col_order is not None: return self._max_col_order self._max_col_order = self._maximal_order(self.col_ineq_graph()) return self._max_col_order - @staticmethod - def _map_gridded_perm(cell_map, gp): - """ - Transform a gridded perm by mapping the position of the gridded perm - according to the cell_map - """ - pos = (cell_map[p] for p in gp.pos) - gp = gp.__class__(gp.patt, pos) - return gp - - def separable(self): + def separable(self) -> bool: """ Test if the tiling is separable. @@ -468,13 +459,13 @@ def separable(self): ncol, nrow = self._tiling.dimensions return len(self.max_row_order) > nrow or len(self.max_col_order) > ncol - def separated_tiling(self): + def separated_tiling(self) -> "Tiling": """ Return the one the possible maximal separation of the tiling. """ return self._separates_tiling(self.max_row_order, self.max_col_order) - def get_cell_map(self) -> Dict[Cell, Cell]: + def get_cell_map(self) -> CellMap: """ Return the position of the according to the given row_order and col_order. This accounts for any cleaning happening inside tiling initializer. @@ -486,16 +477,9 @@ def get_cell_map(self) -> Dict[Cell, Cell]: col_order = self.max_col_order sep_cell_map = self._get_cell_map(row_order, col_order) init_cell_map = sep_tiling.forward_map - res: Dict[Cell, Cell] = dict() - for cell in self._tiling.active_cells: - mid_cell = sep_cell_map[cell] - # If the cell is not in the init map it is an empty cell - if init_cell_map.is_mappable_cell(mid_cell): - final_cell = init_cell_map.map_cell(mid_cell) - res[cell] = final_cell - return res + return sep_cell_map.compose(init_cell_map) - def all_separated_tiling(self, only_max=False): + def all_separated_tiling(self, only_max: bool = False) -> Iterator["Tiling"]: """ Generator over all the possibles separation of the tiling. @@ -546,22 +530,14 @@ def separated_tiling(self) -> "Tiling": return self._tiling return self._separated_tilings[-1] - def get_cell_map(self) -> Dict[Cell, Cell]: + def get_cell_map(self) -> CellMap: """ Return the cell map obtained by applying the algorithm until no change. """ + cell_map = CellMap.identity(self._tiling.dimensions) separation_algo = _RowColSeparationSingleApplication(self._tiling) - cell_maps = [] while separation_algo.separable(): - cell_map = separation_algo.get_cell_map() - cell_maps.append(cell_map) + cell_map = cell_map.compose(separation_algo.get_cell_map()) new_sep = separation_algo.separated_tiling() separation_algo = _RowColSeparationSingleApplication(new_sep) - res = {cell: cell for cell in self._tiling.active_cells} - for cell_map in cell_maps: - for cell, mapped_cell in tuple(res.items()): - if mapped_cell in cell_map: - res[cell] = cell_map[mapped_cell] - else: - res.pop(cell) - return res + return cell_map diff --git a/tilings/map.py b/tilings/map.py index cbd8a782..ed0fd7f5 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -2,14 +2,177 @@ from typing import TYPE_CHECKING, Dict, Iterator, Optional, Tuple from tilings.exception import InvalidOperationError +from tilings.griddedperm import GriddedPerm if TYPE_CHECKING: - from tilings.griddedperm import GriddedPerm + from tilings.parameter_counter import ParameterCounter, PreimageCounter + from tilings.tiling import Tiling Cell = Tuple[int, int] -class RowColMap: +class CellMap: + def __init__(self, map: Dict[Cell, Cell]) -> None: + self._map = map + + @classmethod + def identity(cls, dimensions: Tuple[int, int]) -> "CellMap": + cells = itertools.product(range(dimensions[0]), range(dimensions[1])) + return CellMap({c: c for c in cells}) + + def inverse(self) -> "CellMap": + """ + Return the inverse map if possible. + Otherwise raise an InvalidOperationError. + """ + inverse_map = {v: k for k, v in self._map.items()} + if len(inverse_map) != len(self._map): + raise InvalidOperationError("The map is not reversible.") + return CellMap(inverse_map) + + def to_row_col_map(self) -> "RowColMap": + """ + Convert the CellMap object into an equivalent RowColMap object. + + Raises InvalidOperationError if the columns or row are not mapped consistently + and therefore the conversion can't be completed. + """ + col_map, row_map = dict(), dict() + for (col, row), (new_col, new_row) in self._map.items(): + if col not in col_map: + col_map[col] = new_col + elif col_map[col] != new_col: + raise InvalidOperationError("Not mapping column consistently.") + if row not in row_map: + row_map[row] = new_row + elif row_map[row] != new_row: + raise InvalidOperationError("Not mapping row consistently.") + return RowColMap(col_map=col_map, row_map=row_map) + + def compose(self, other: "CellMap") -> "CellMap": + """ + The return the new map that is obtained by the applying first self and then + other. + + If self maps a -> b and other maps b -> c than the resulting map maps a -> c. + """ + return CellMap( + { + k: other.map_cell(v) + for k, v in self._map.items() + if other.is_mappable_cell(v) + } + ) + + # Mapping method + def map_tiling(self, tiling: "Tiling") -> "Tiling": + """ + Map the tiling according to the map. + + Point obstruction that cannot be mapped are ignored. + """ + mapped_obs = ( + self.map_gp(ob) + for ob in tiling.obstructions + if not ob.is_point_perm() or self.is_mappable_gp(ob) + ) + obs = itertools.filterfalse(GriddedPerm.contradictory, mapped_obs) + reqs = ( + itertools.filterfalse(GriddedPerm.contradictory, map(self.map_gp, req_list)) + for req_list in tiling.requirements + ) + params = map(self.map_param, tiling.parameters) + return tiling.__class__(obs, reqs, params) + + def map_param(self, param: "ParameterCounter") -> "ParameterCounter": + """ + Map the parameter of according to the map. + """ + return param.__class__( + (self.map_preimage_counter(preimg_counter) for preimg_counter in param) + ) + + def map_preimage_counter( + self, + preimg_counter: "PreimageCounter", + ) -> "PreimageCounter": + """ + Maps the given counter according to the map. + + NOTE: This works if the map is bijective. Not sure about other cases. + """ + cell_pos_in_col, cell_pos_in_row = dict(), dict() + col_split = [0 for _ in range(preimg_counter.tiling.dimensions[0])] + row_split = [0 for _ in range(preimg_counter.tiling.dimensions[1])] + for cell in self._map: + for pre_cell in preimg_counter.map.preimage_cell(cell): + col_pos = self.map_cell(cell)[0] - cell[0] + row_pos = self.map_cell(cell)[1] - cell[1] + cell_pos_in_col[pre_cell] = col_pos + cell_pos_in_row[pre_cell] = row_pos + col_split[cell[0]] = max(col_pos + 1, col_split[cell[0]]) + row_split[cell[1]] = max(row_pos + 1, row_split[cell[1]]) + cell_to_col_map = { + k: v + sum(col_split[: k[0]]) for k, v in cell_pos_in_col.items() + } + cell_to_row_map = { + k: v + sum(row_split[: k[1]]) for k, v in cell_pos_in_row.items() + } + preimg_map = CellMap( + { + cell: (cell_to_col_map[cell], cell_to_row_map[cell]) + for cell in preimg_counter.tiling.active_cells + } + ) + projection_map = ( + preimg_map.inverse() + .compose(preimg_counter.map) + .compose(self) + .to_row_col_map() + ) + return preimg_counter.__class__( + preimg_map.map_tiling(preimg_counter.tiling), projection_map + ) + + def is_mappable_gp(self, gp: "GriddedPerm") -> bool: + """ + Return True if all the cell used by the gridded perm can be mapped. + """ + return all(self.is_mappable_cell(cell) for cell in gp.pos) + + def map_gp(self, gp: "GriddedPerm") -> "GriddedPerm": + """ + Map the gridded permutation according to the map. + """ + return gp.__class__(gp.patt, map(self.map_cell, gp.pos)) + + def is_mappable_cell(self, cell: Cell) -> bool: + """ + Return True if the cell can be mapped. + """ + return cell in self._map + + def map_cell(self, cell: Cell) -> Cell: + """ + Map the cell according to the map. + """ + return self._map[cell] + + def __str__(self) -> str: + cells = [f"{k}: {v}" for k, v in sorted(self._map.items())] + cells_str = ", ".join(cells) + return f"Cell Map: {{{cells_str}}}" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CellMap): + return NotImplemented + return self._map == other._map + + def __hash__(self) -> int: + return hash(tuple(sorted(self._map.items()))) + + +class RowColMap(CellMap): """ A class to combine a row and a column map together and map different object related to tiling in accordance to those row and columns map. @@ -42,9 +205,9 @@ def identity(cls, dimensions: Tuple[int, int]) -> "RowColMap": row_map = {i: i for i in range(dimensions[1])} return RowColMap(row_map=row_map, col_map=col_map, is_identity=True) - def reverse(self) -> "RowColMap": + def inverse(self) -> "RowColMap": """ - Return the reverse map if possible. + Return the inverse map if possible. Otherwise raise an InvalidOperationError. """ row_map = {v: k for k, v in self._row_map.items()} @@ -55,13 +218,18 @@ def reverse(self) -> "RowColMap": row_map=row_map, col_map=col_map, is_identity=self._is_identity ) - def compose(self, other: "RowColMap") -> "RowColMap": + def to_row_col_map(self) -> "RowColMap": + return self + + def compose(self, other: "CellMap") -> "RowColMap": """ The return the new map that is obtained by the applying first self and then other. If self maps a -> b and other maps b -> c than the resulting map maps a -> c. """ + if not isinstance(other, RowColMap): + raise NotImplementedError col_map = {k: other.map_col(v) for k, v in self._col_map.items()} row_map = {k: other.map_row(v) for k, v in self._row_map.items()} return RowColMap(row_map=row_map, col_map=col_map) @@ -84,18 +252,6 @@ def is_non_crossing(self) -> bool: return cols == sorted(cols) and rows == sorted(rows) # Mapping method - def is_mappable_gp(self, gp: "GriddedPerm") -> bool: - """ - Return True if all the cell used by the gridded perm can be mapped. - """ - return all(self.is_mappable_cell(cell) for cell in gp.pos) - - def map_gp(self, gp: "GriddedPerm") -> "GriddedPerm": - """ - Map the gridded permutation according to the map. - """ - return gp.__class__(gp.patt, map(self.map_cell, gp.pos)) - def is_mappable_cell(self, cell: Cell) -> bool: """ Return True if the cell can be mapped, i.e. if the image of the row diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 40435f7b..c5a54a32 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -25,7 +25,6 @@ def _init_checked(self): """ Some sanity check on the counter. """ - assert self.map.is_non_crossing() assert not self.tiling.parameters def remove_empty_rows_and_cols(self) -> None: @@ -172,3 +171,6 @@ def __repr__(self) -> str: def __str__(self): return "".join(map(str, self.counters)) + + def __iter__(self) -> Iterator[PreimageCounter]: + return iter(self.counters) diff --git a/tilings/strategies/row_and_col_separation.py b/tilings/strategies/row_and_col_separation.py index 457537d5..7bf19b45 100644 --- a/tilings/strategies/row_and_col_separation.py +++ b/tilings/strategies/row_and_col_separation.py @@ -8,12 +8,12 @@ from comb_spec_searcher.exception import StrategyDoesNotApply from tilings import GriddedPerm, Tiling from tilings.algorithms import RowColSeparation +from tilings.map import CellMap __all__ = ["RowColumnSeparationStrategy"] Cell = Tuple[int, int] -CellMap = Dict[Cell, Cell] class RowColumnSeparationStrategy(DisjointUnionStrategy[Tiling, GriddedPerm]): @@ -38,7 +38,7 @@ def _get_cell_maps(self, tiling: Tiling) -> Tuple[CellMap, CellMap]: res = self._cell_maps.get(tiling) if res is None: forward_cell_map = self.row_col_sep_algorithm(tiling).get_cell_map() - backward_cell_map = {y: x for x, y in forward_cell_map.items()} + backward_cell_map = forward_cell_map.inverse() self._cell_maps[tiling] = forward_cell_map, backward_cell_map else: forward_cell_map, backward_cell_map = res @@ -60,25 +60,16 @@ def extra_parameters( if children is None: raise StrategyDoesNotApply("Strategy does not apply") child = children[0] - mapped_assumptions = tuple( - ass.__class__( - tuple( - self.forward_map(comb_class, gp, children)[0] - for gp in ass.gps - if all(cell in self.forward_cell_map(comb_class) for cell in gp.pos) - ) - ).avoiding(child.obstructions) - for ass in comb_class.assumptions - ) + forward_map = self.forward_cell_map(comb_class) + mapped_params = tuple(map(forward_map.map_param, comb_class.parameters)) return ( { - comb_class.get_assumption_parameter( - assumption - ): child.get_assumption_parameter(mapped_assumption) - for assumption, mapped_assumption in zip( - comb_class.assumptions, mapped_assumptions + comb_class.get_parameter_name(param): child.get_parameter_name( + mapped_param ) - if mapped_assumption.gps + for param, mapped_param in zip(comb_class.parameters, mapped_params) + # TODO: previsously checked that the tracked assumption had some gps + # here. What is the equivalence in our case. }, ) @@ -103,8 +94,7 @@ def backward_map( children = self.decomposition_function(tiling) gp = gps[0] assert gp is not None - backmap = self.backward_cell_map(tiling) - yield gp.apply_map(backmap.__getitem__) + yield self.backward_cell_map(tiling).map_gp(gp) def forward_map( self, @@ -115,9 +105,7 @@ def forward_map( """This function will enable us to have a quick membership test.""" if children is None: children = self.decomposition_function(tiling) - forwardmap = self.forward_cell_map(tiling) - gp = gp.apply_map(forwardmap.__getitem__) - return (gp,) + return (self.forward_cell_map(tiling).map_gp(gp),) def __str__(self) -> str: return "row and column separation" diff --git a/tilings/tiling.py b/tilings/tiling.py index b2335ff5..e8749155 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -47,7 +47,7 @@ from .exception import InvalidOperationError from .griddedperm import GriddedPerm from .gui_launcher import run_gui -from .map import RowColMap +from .map import CellMap, RowColMap from .misc import intersection_reduce, union_reduce from .parameter_counter import ParameterCounter @@ -838,7 +838,7 @@ def backward_map(self) -> RowColMap: try: return self._cached_properties["backward_map"] except KeyError: - backward_map = self.forward_map.reverse() + backward_map = self.forward_map.inverse() self._cached_properties["backward_map"] = backward_map return backward_map @@ -1091,7 +1091,7 @@ def row_and_column_separation(self) -> "Tiling": def row_and_column_separation_with_mapping( self, - ) -> Tuple["Tiling", Dict[Cell, Cell]]: + ) -> Tuple["Tiling", CellMap]: rcs = RowColSeparation(self) return rcs.separated_tiling(), rcs.get_cell_map() From ca64da3b917563f6528e34321d3c394be12f4e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Wed, 6 Oct 2021 08:08:12 -0400 Subject: [PATCH 09/41] compute pre-image of tiling according to row col map (#363) --- tests/test_map.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++- tilings/map.py | 12 ++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/tests/test_map.py b/tests/test_map.py index 46a6d310..8aadc49d 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -1,6 +1,6 @@ import pytest -from tilings.griddedperm import GriddedPerm +from tilings import GriddedPerm, Tiling from tilings.map import RowColMap @@ -60,3 +60,57 @@ def test_preimage_gp(double_all_map): GriddedPerm((0, 1), ((1, 0), (1, 1))), GriddedPerm((0, 1), ((1, 1), (1, 1))), ] + + +def test_preimage_tiling(double_row_map, double_col_map, double_all_map): + t1 = Tiling( + obstructions=[ + GriddedPerm((0, 2, 1), ((0, 0),) * 3), + ], + requirements=[[GriddedPerm((0,), ((0, 0),))]], + ) + assert double_row_map.preimage_tiling(t1) == Tiling( + [ + GriddedPerm((0, 2, 1), [(0, 0), (0, 0), (0, 0)]), + GriddedPerm((0, 2, 1), [(0, 0), (0, 1), (0, 0)]), + GriddedPerm((0, 2, 1), [(0, 0), (0, 1), (0, 1)]), + GriddedPerm((0, 2, 1), [(0, 1), (0, 1), (0, 1)]), + ], + [[GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((0, 1),))]], + ) + assert double_col_map.preimage_tiling(t1) == Tiling( + [ + GriddedPerm((0, 2, 1), [(0, 0), (0, 0), (0, 0)]), + GriddedPerm((0, 2, 1), [(0, 0), (0, 0), (1, 0)]), + GriddedPerm((0, 2, 1), [(0, 0), (1, 0), (1, 0)]), + GriddedPerm((0, 2, 1), [(1, 0), (1, 0), (1, 0)]), + ], + [[GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))]], + ) + t2 = Tiling( + obstructions=[ + GriddedPerm((0, 1), ((0, 0),) * 2), + ], + requirements=[[GriddedPerm((0,), ((0, 0),))]], + ) + assert double_all_map.preimage_tiling(t2) == Tiling( + [ + GriddedPerm((0, 1), [(0, 0), (0, 0)]), + GriddedPerm((0, 1), [(0, 0), (0, 1)]), + GriddedPerm((0, 1), [(0, 0), (1, 0)]), + GriddedPerm((0, 1), [(0, 0), (1, 1)]), + GriddedPerm((0, 1), [(0, 1), (0, 1)]), + GriddedPerm((0, 1), [(0, 1), (1, 1)]), + GriddedPerm((0, 1), [(1, 0), (1, 0)]), + GriddedPerm((0, 1), [(1, 0), (1, 1)]), + GriddedPerm((0, 1), [(1, 1), (1, 1)]), + ], + [ + [ + GriddedPerm.point_perm((0, 0)), + GriddedPerm.point_perm((1, 0)), + GriddedPerm.point_perm((0, 1)), + GriddedPerm.point_perm((1, 1)), + ] + ], + ) diff --git a/tilings/map.py b/tilings/map.py index ed0fd7f5..4e71d599 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -314,6 +314,18 @@ def preimage_gp(self, gp: "GriddedPerm") -> Iterator["GriddedPerm"]: if not new_gp.contradictory(): yield new_gp + def preimage_tiling(self, tiling: "Tiling") -> "Tiling": + if tiling.parameters: + raise NotImplementedError("Not implemented for tilings with parameter") + obs = itertools.chain.from_iterable( + self.preimage_gp(ob) for ob in tiling.obstructions + ) + reqs = ( + itertools.chain.from_iterable(self.preimage_gp(req) for req in req_list) + for req_list in tiling.requirements + ) + return tiling.__class__(obs, reqs) + # Other method def max_row(self) -> int: """Return the biggest row index in the image.""" From f53ac0312d1189166f8518f92613be825b6d3101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 14 Oct 2021 07:22:51 -0400 Subject: [PATCH 10/41] Activate row column test (#368) * update row col map test with new param * infer empty row and col on preimage based on underlying * map parameter properly * activate row col strategy test --- tests/strategies/test_inferral.py | 2 -- tests/strategies/test_point_placements.py | 2 -- .../strategies/test_row_column_separation.py | 18 +++++------ tilings/algorithms/row_col_separation.py | 32 ++++++++++++++++--- tilings/parameter_counter.py | 14 +++++++- tilings/strategies/row_and_col_separation.py | 4 +-- tox.ini | 2 +- 7 files changed, 51 insertions(+), 23 deletions(-) diff --git a/tests/strategies/test_inferral.py b/tests/strategies/test_inferral.py index 6504fa65..bba0d6ba 100644 --- a/tests/strategies/test_inferral.py +++ b/tests/strategies/test_inferral.py @@ -9,8 +9,6 @@ RowColumnSeparationStrategy, ) -pytest_plugins = ["tests.fixtures.simple_trans"] - @pytest.fixture def tiling1(): diff --git a/tests/strategies/test_point_placements.py b/tests/strategies/test_point_placements.py index a6bcf192..dac0bcbe 100644 --- a/tests/strategies/test_point_placements.py +++ b/tests/strategies/test_point_placements.py @@ -13,8 +13,6 @@ from tilings.strategies.requirement_placement import RequirementPlacementStrategy pytest_plugins = [ - "tests.fixtures.obstructions_requirements", - "tests.fixtures.simple_tiling", "tests.fixtures.diverse_tiling", "tests.fixtures.no_point_tiling", ] diff --git a/tests/strategies/test_row_column_separation.py b/tests/strategies/test_row_column_separation.py index e0698c45..b3c29fc5 100644 --- a/tests/strategies/test_row_column_separation.py +++ b/tests/strategies/test_row_column_separation.py @@ -5,11 +5,10 @@ from comb_spec_searcher.strategies import Rule from tilings import GriddedPerm, Tiling from tilings.algorithms import RowColSeparation -from tilings.assumptions import TrackingAssumption +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategies import RowColumnSeparationStrategy -pytest_plugins = ["tests.fixtures.simple_trans"] - # Row column separation test @pytest.fixture @@ -309,8 +308,8 @@ def test_maps(): assert next(rule.backward_map(gps)) == GriddedPerm((0,), (image,)) -def test_mapping_assumptions(): - tiling = Tiling( +def test_mapping_parameter(): + baset = Tiling( obstructions=( GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((2, 1),)), @@ -328,11 +327,10 @@ def test_mapping_assumptions(): GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (1, 0))), ), requirements=((GriddedPerm((0,), ((1, 1),)),),), - assumptions=( - TrackingAssumption( - (GriddedPerm((0,), ((1, 0),)), GriddedPerm((0,), ((1, 1),))) - ), - ), + ) + rc_map = RowColMap(row_map={0: 0, 1: 1}, col_map={0: 0, 1: 1, 2: 1, 3: 2}) + tiling = baset.add_parameter( + ParameterCounter([PreimageCounter(rc_map.preimage_tiling(baset), rc_map)]) ) strategy = RowColumnSeparationStrategy() rule = strategy(tiling) diff --git a/tilings/algorithms/row_col_separation.py b/tilings/algorithms/row_col_separation.py index 6a9d27a0..11263438 100644 --- a/tilings/algorithms/row_col_separation.py +++ b/tilings/algorithms/row_col_separation.py @@ -26,6 +26,7 @@ if TYPE_CHECKING: from tilings import GriddedPerm, Tiling + from tilings.parameter_counter import ParameterCounter Cell = Tuple[int, int] Edge = Tuple[int, int] @@ -465,17 +466,24 @@ def separated_tiling(self) -> "Tiling": """ return self._separates_tiling(self.max_row_order, self.max_col_order) + def seperation_map(self) -> CellMap: + """ + Return the position of map from the orginal tiling to the seperated tiling. + + This does not account for rows or column becoming empty. + """ + row_order = self.max_row_order + col_order = self.max_col_order + return self._get_cell_map(row_order, col_order) + def get_cell_map(self) -> CellMap: """ - Return the position of the according to the given row_order and - col_order. This accounts for any cleaning happening inside tiling initializer. + Return the position of map from the orginal tiling to the seperated tiling. This is the cell map for the separated tiling returned by `separated_tiling`. """ sep_tiling = self.separated_tiling() - row_order = self.max_row_order - col_order = self.max_col_order - sep_cell_map = self._get_cell_map(row_order, col_order) + sep_cell_map = self.seperation_map() init_cell_map = sep_tiling.forward_map return sep_cell_map.compose(init_cell_map) @@ -541,3 +549,17 @@ def get_cell_map(self) -> CellMap: new_sep = separation_algo.separated_tiling() separation_algo = _RowColSeparationSingleApplication(new_sep) return cell_map + + def map_param(self, param: "ParameterCounter") -> "ParameterCounter": + """ + Map the parameter the parent tiling to the corresponding parameters on the + child. + """ + separation_algo = _RowColSeparationSingleApplication(self._tiling) + while separation_algo.separable(): + new_sep = separation_algo.separated_tiling() + separation_map = separation_algo.seperation_map() + param = separation_map.map_param(param) + param.apply_row_col_map(new_sep.forward_map) + separation_algo = _RowColSeparationSingleApplication(new_sep) + return param diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index c5a54a32..2669bd72 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -37,7 +37,19 @@ def remove_empty_rows_and_cols(self) -> None: def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": """ Modify in place the map with respect to the given row_col_map. Return self. - """ + + If some of the row/col from the preimage tiling can't be mapped by the + composition, then they'll be made empty on the preimage tiling. + """ + empty_cells = [ + cell + for cell in self.tiling.active_cells + if not row_col_map.is_mappable_cell(self.map.map_cell(cell)) + ] + if empty_cells: + new_obs = map(GriddedPerm.point_perm, empty_cells) + self.tiling = self.tiling.add_obstructions(new_obs) + self.map = self.tiling.backward_map.compose(self.map) self.map = self.map.compose(row_col_map) return self diff --git a/tilings/strategies/row_and_col_separation.py b/tilings/strategies/row_and_col_separation.py index 7bf19b45..ebe853fd 100644 --- a/tilings/strategies/row_and_col_separation.py +++ b/tilings/strategies/row_and_col_separation.py @@ -60,8 +60,8 @@ def extra_parameters( if children is None: raise StrategyDoesNotApply("Strategy does not apply") child = children[0] - forward_map = self.forward_cell_map(comb_class) - mapped_params = tuple(map(forward_map.map_param, comb_class.parameters)) + algo = self.row_col_sep_algorithm(comb_class) + mapped_params = tuple(map(algo.map_param, comb_class.parameters)) return ( { comb_class.get_parameter_name(param): child.get_parameter_name( diff --git a/tox.ini b/tox.ini index 01b42b59..6d8f89a2 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore-glob=tests/strategies/ +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_sanity_check.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_point_placements.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From 48da4c2c9a419037c3240a1cb61a821f926bf736 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 15 Oct 2021 08:33:30 +0000 Subject: [PATCH 11/41] deleting comp fusion, and hopefully tests now "work" (#370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * deleting comp fusion, and hopefully tests now "work" * remove DS_Store file Co-authored-by: Émile Nadeau --- .gitignore | 1 + tests/algorithms/test_fusion.py | 8 +- tests/algorithms/test_sliding_alg.py | 20 -- tests/strategies/test_encoding.py | 7 +- tests/strategies/test_fusion_strat.py | 18 +- tests/test_assumptions.py | 26 +-- tests/test_tilescope.py | 25 ++- tilings/algorithms/__init__.py | 3 +- tilings/algorithms/fusion.py | 224 -------------------- tilings/algorithms/general_fusion.py | 1 + tilings/algorithms/requirement_placement.py | 3 +- tilings/map.py | 10 +- tilings/strategies/__init__.py | 3 +- tilings/strategies/assumption_splitting.py | 4 - tilings/strategies/fusion/__init__.py | 3 - tilings/strategies/fusion/component.py | 87 -------- tilings/strategies/verification.py | 30 --- tilings/strategy_pack.py | 32 +-- tilings/tiling.py | 17 +- 19 files changed, 68 insertions(+), 454 deletions(-) delete mode 100644 tilings/strategies/fusion/component.py diff --git a/.gitignore b/.gitignore index c155cb4d..2e231f76 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,4 @@ devtest.py .pytest_cache .mypy_cache .vscode/ +.DS_Store diff --git a/tests/algorithms/test_fusion.py b/tests/algorithms/test_fusion.py index 768cc6c7..0b5ffb57 100644 --- a/tests/algorithms/test_fusion.py +++ b/tests/algorithms/test_fusion.py @@ -2,12 +2,17 @@ from permuta import Perm from tilings import GriddedPerm, Tiling -from tilings.algorithms import ComponentFusion, Fusion +from tilings.algorithms import Fusion from tilings.assumptions import TrackingAssumption pytestmark = pytest.mark.xfail +class ComponentFusion: + # delete me please + pass + + class TestFusion: @pytest.fixture def small_tiling(self): @@ -321,6 +326,7 @@ def test_positive_fusion(self): assert algo.min_left_right_points() == (1, 0) +@pytest.mark.xfail class TestComponentFusion(TestFusion): @pytest.fixture def col_tiling(self): diff --git a/tests/algorithms/test_sliding_alg.py b/tests/algorithms/test_sliding_alg.py index 1f9d7f9b..477bf235 100644 --- a/tests/algorithms/test_sliding_alg.py +++ b/tests/algorithms/test_sliding_alg.py @@ -25,7 +25,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (2, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -39,7 +38,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), ), requirements=(), - assumptions=(), ), ), ( @@ -104,7 +102,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((3, 0, 1, 2), ((1, 0), (1, 0), (2, 0), (2, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -115,7 +112,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((1, 2, 3, 0), ((0, 0), (0, 0), (1, 0), (1, 0))), ), requirements=(), - assumptions=(), ), ), ( @@ -126,7 +122,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2, 3), ((0, 0), (1, 0), (1, 0), (1, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -135,7 +130,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (1, 0))), ), requirements=(), - assumptions=(), ), ), ( @@ -152,7 +146,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2, 3), ((1, 0), (3, 0), (3, 0), (3, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -167,7 +160,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2, 3), ((3, 0), (3, 0), (3, 0), (3, 0))), ), requirements=(), - assumptions=(), ), ), ( @@ -227,7 +219,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), ), requirements=(), - assumptions=(), ), None, ), @@ -240,7 +231,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1), ((1, 0), (2, 0))), ), requirements=(), - assumptions=(), ), None, ), @@ -253,7 +243,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1), ((0, 0), (2, 0))), ), requirements=(), - assumptions=(), ), None, ), @@ -266,7 +255,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), ), requirements=(), - assumptions=(), ), None, ), @@ -279,7 +267,6 @@ def generate_all_slided_tilings(tiling): GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), ), requirements=(), - assumptions=(), ), None, ), @@ -314,7 +301,6 @@ def test_algorithms_sliding(): GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), ), requirements=(), - assumptions=(), ), } @@ -329,7 +315,6 @@ def test_algorithms_sliding(): GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (3, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -341,7 +326,6 @@ def test_algorithms_sliding(): GriddedPerm((0, 1, 2), ((3, 0), (3, 0), (3, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -353,7 +337,6 @@ def test_algorithms_sliding(): GriddedPerm((0, 1, 2), ((3, 0), (3, 0), (3, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -365,7 +348,6 @@ def test_algorithms_sliding(): GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (3, 0))), ), requirements=(), - assumptions=(), ), ] @@ -397,7 +379,6 @@ def test_slide(): GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (2, 0))), ), requirements=(), - assumptions=(), ), Tiling( obstructions=( @@ -411,7 +392,6 @@ def test_slide(): GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), ), requirements=(), - assumptions=(), ), (1, 2), 7, diff --git a/tests/strategies/test_encoding.py b/tests/strategies/test_encoding.py index 370fa24d..5f7567c7 100644 --- a/tests/strategies/test_encoding.py +++ b/tests/strategies/test_encoding.py @@ -12,7 +12,6 @@ AllPlacementsFactory, BasicVerificationStrategy, CellInsertionFactory, - ComponentFusionFactory, DatabaseVerificationStrategy, ElementaryVerificationStrategy, EmptyCellInferralFactory, @@ -47,7 +46,7 @@ FactorWithInterleavingStrategy, FactorWithMonotoneInterleavingStrategy, ) -from tilings.strategies.fusion import ComponentFusionStrategy, FusionStrategy +from tilings.strategies.fusion import FusionStrategy from tilings.strategies.obstruction_inferral import ObstructionInferralStrategy from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy from tilings.strategies.requirement_insertion import RequirementInsertionStrategy @@ -388,11 +387,7 @@ def short_length_arguments(strategy): + [RowColumnSeparationStrategy(), SubobstructionInferralFactory()] + [FusionStrategy(row_idx=1)] + [FusionStrategy(col_idx=3)] - + [ComponentFusionStrategy(row_idx=1)] - + [ComponentFusionStrategy(col_idx=3)] - + [ComponentFusionStrategy(col_idx=3)] + [FusionFactory()] - + [ComponentFusionFactory()] + [ObstructionInferralStrategy([GriddedPerm((0, 1, 2), ((0, 0), (1, 1), (1, 2)))])] + [ SplittingStrategy(), diff --git a/tests/strategies/test_fusion_strat.py b/tests/strategies/test_fusion_strat.py index 8e7d7334..f646d559 100644 --- a/tests/strategies/test_fusion_strat.py +++ b/tests/strategies/test_fusion_strat.py @@ -5,12 +5,16 @@ from tilings import GriddedPerm, Tiling from tilings.algorithms import Fusion from tilings.assumptions import TrackingAssumption -from tilings.strategies import ComponentFusionFactory, FusionFactory -from tilings.strategies.fusion import ( - ComponentFusionStrategy, - FusionConstructor, - FusionStrategy, -) +from tilings.strategies import FusionFactory +from tilings.strategies.fusion import FusionConstructor, FusionStrategy + + +class ComponentFusionStrategy: + # delete me + pass + + +ComponentFusionFactory = ComponentFusionStrategy @pytest.fixture @@ -48,6 +52,7 @@ def tiling2(): return t +@pytest.mark.xfail def test_component_fusion(tiling1, tiling2): assert len(list(ComponentFusionFactory()(tiling1))) == 0 assert len(list(ComponentFusionFactory()(tiling2))) == 1 @@ -191,6 +196,7 @@ def component_col_fusion(col_tiling): return ComponentFusionStrategy(col_idx=0, tracked=True)(col_tiling) +@pytest.mark.xfail def test_formal_step_component(component_col_fusion, component_row_fusion): assert component_col_fusion.formal_step == "component fuse columns 0 and 1" assert component_row_fusion.formal_step == "component fuse rows 0 and 1" diff --git a/tests/test_assumptions.py b/tests/test_assumptions.py index 0b00ca38..b37737e9 100644 --- a/tests/test_assumptions.py +++ b/tests/test_assumptions.py @@ -1,7 +1,6 @@ import json import os import pickle -from operator import xor import pytest @@ -9,11 +8,7 @@ from permuta import Av, Perm from tilings import GriddedPerm, Tiling from tilings.algorithms import Factor -from tilings.assumptions import ( - SkewComponentAssumption, - SumComponentAssumption, - TrackingAssumption, -) +from tilings.assumptions import TrackingAssumption from tilings.strategy_pack import TileScopePack from tilings.tilescope import TileScope @@ -113,25 +108,6 @@ def test_factors(tplaced_tracked, tplaced_tracked_factored1, tplaced_tracked_fac ) -def test_order(): - sum_ass = SumComponentAssumption( - (GriddedPerm((0,), ((1, 1),)), GriddedPerm((0,), ((2, 0),))) - ) - skew_ass = SkewComponentAssumption( - ( - GriddedPerm((0,), ((1, 1),)), - GriddedPerm((0,), ((2, 0),)), - GriddedPerm((0,), ((2, 2),)), - ) - ) - point_ass = TrackingAssumption([GriddedPerm.single_cell((0,), (0, 0))]) - point_ass2 = TrackingAssumption([GriddedPerm.single_cell((0,), (2, 0))]) - assert point_ass < point_ass2 and not point_ass2 < point_ass - assert xor(sum_ass < skew_ass, skew_ass < sum_ass) - assert xor(sum_ass < point_ass, point_ass < sum_ass) - assert xor(point_ass < skew_ass, skew_ass < point_ass) - - def test_from_cell(): assert TrackingAssumption.from_cells([]) == TrackingAssumption([]) assert TrackingAssumption.from_cells([(0, 1)]) == TrackingAssumption( diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index 6b2125af..58de4e0b 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -8,10 +8,16 @@ from permuta import Av, Perm from tilings import GriddedPerm, Tiling from tilings import strategies as strat -from tilings.strategies.fusion import ComponentFusionStrategy, FusionStrategy +from tilings.strategies.fusion import FusionStrategy from tilings.strategy_pack import TileScopePack from tilings.tilescope import GuidedSearcher, TileScope + +class ComponentFusionStrategy: + # delete me + pass + + pytestmark = pytest.mark.xfail point_placements = TileScopePack.point_placements() @@ -20,17 +26,9 @@ tracked=False ) point_placements_fusion = point_placements.make_fusion(tracked=False) -point_placements_component_fusion = point_placements.make_fusion( - component=True, tracked=False -) row_placements_fusion = TileScopePack.row_and_col_placements(row_only=True).make_fusion( tracked=True ) -row_and_col_placements_component_fusion_fusion = ( - TileScopePack.row_and_col_placements() - .make_fusion(component=True, tracked=False) - .make_fusion(tracked=False) -) reginsenc = TileScopePack.regular_insertion_encoding(3) @@ -124,6 +122,9 @@ def test_123_with_db(): @pytest.mark.timeout(20) def test_1342_1423(): + point_placements_component_fusion = point_placements.make_fusion( + component=True, tracked=False + ) searcher = TileScope("1342_1423", point_placements_component_fusion) spec = searcher.auto_search(smallest=True) assert spec.number_of_rules() == 9 @@ -183,8 +184,14 @@ def test_reverse_equiv(): assert spec.random_sample_object_of_size(i).patt in av +@pytest.mark.xfail @pytest.mark.timeout(20) def test_1324(): + row_and_col_placements_component_fusion_fusion = ( + TileScopePack.row_and_col_placements() + .make_fusion(component=True, tracked=False) + .make_fusion(tracked=False) + ) searcher = TileScope("1324", row_and_col_placements_component_fusion_fusion) spec = searcher.auto_search(smallest=True) assert spec.number_of_rules() == 9 diff --git a/tilings/algorithms/__init__.py b/tilings/algorithms/__init__.py index baba63d2..d72c23a6 100644 --- a/tilings/algorithms/__init__.py +++ b/tilings/algorithms/__init__.py @@ -1,6 +1,6 @@ from .enumeration import DatabaseEnumeration, LocalEnumeration, MonotoneTreeEnumeration from .factor import Factor, FactorWithInterleaving, FactorWithMonotoneInterleaving -from .fusion import ComponentFusion, Fusion +from .fusion import Fusion from .gridded_perm_generation import GriddedPermsOnTiling from .gridded_perm_reduction import GriddedPermReduction from .guess_obstructions import guess_obstructions @@ -23,7 +23,6 @@ "Factor", "FactorWithInterleaving", "FactorWithMonotoneInterleaving", - "ComponentFusion", "Fusion", "MinimalGriddedPerms", "AllObstructionInferral", diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 3c9b518a..a1e45a67 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -2,7 +2,6 @@ The implementation of the fusion algorithm """ import collections -from itertools import chain from typing import TYPE_CHECKING, Counter, Iterable, Iterator, List, Optional, Tuple from tilings.assumptions import TrackingAssumption @@ -273,10 +272,6 @@ def _can_fuse_assumption( are all contained entirely on the left of the fusion region, entirely on the right, or split in every possible way. """ - if isinstance(assumption, ComponentAssumption): - return self.is_left_sided_assumption( - assumption - ) and self.is_right_sided_assumption(assumption) return self._can_fuse_set_of_gridded_perms(fuse_counter) or ( all(count == 1 for gp, count in fuse_counter.items()) and self._is_one_sided_assumption(assumption) @@ -398,222 +393,3 @@ def fused_tiling(self) -> "Tiling": assumptions=assumptions, ) return self._fused_tiling - - -class ComponentFusion(Fusion): - """ - Component Fusion algorithm container class. - - Fuse tiling it it can be unfused by drawing a line between any component. - - Check if a fusion is valid and compute the fused tiling. - - If `row_idx` is provided it attempts to fuse row `row_idx` with row - `row_idx+1`. - - If `col_idx` is provided it attempts to fuse column `col_idx` with - column `col_idx+1`. - """ - - def __init__( - self, - tiling: "Tiling", - *, - row_idx: Optional[int] = None, - col_idx: Optional[int] = None, - tracked: bool = False, - isolation_level: Optional[str] = None, - ): - if tiling.requirements: - raise NotImplementedError( - "Component fusion does not handle " "requirements at the moment" - ) - super().__init__( - tiling, - row_idx=row_idx, - col_idx=col_idx, - tracked=tracked, - isolation_level=isolation_level, - ) - self._first_cell: Optional[Cell] = None - self._second_cell: Optional[Cell] = None - - def _pre_check(self) -> bool: - """ - Make a preliminary check before testing if the actual fusion is - possible. - - Selects the two active cells to be fused. Rows or columns with more - than one active cell cannot be fused. Sets the attribute - `self._first_cell` and `self._second_cell`. - """ - if self._fuse_row: - rows = ( - self._tiling.cells_in_row(self._row_idx), - self._tiling.cells_in_row(self._row_idx + 1), - ) - else: - rows = ( - self._tiling.cells_in_col(self._col_idx), - self._tiling.cells_in_col(self._col_idx + 1), - ) - has_a_long_row = any(len(row) > 1 for row in rows) - if has_a_long_row: - return False - first_cell = next(iter(rows[0])) - second_cell = next(iter(rows[1])) - cells_are_adjacent = ( - first_cell[0] == second_cell[0] or first_cell[1] == second_cell[1] - ) - if not cells_are_adjacent: - return False - same_basis = ( - self._tiling.cell_basis()[first_cell][0] - == self._tiling.cell_basis()[second_cell][0] - ) - if not same_basis: - return False - self._first_cell = first_cell - self._second_cell = second_cell - return True - - @property - def first_cell(self) -> Cell: - """ - The first cell of the fusion. This cell is in the bottommost row or the - leftmost column of the fusion. - """ - if self._first_cell is not None: - return self._first_cell - if not self._pre_check(): - raise RuntimeError( - "Pre-check failed. No component fusion " "possible and no first cell" - ) - assert self._first_cell is not None - return self._first_cell - - @property - def second_cell(self) -> Cell: - """ - The second cell of the fusion. This cell is in the topmost row or the - rightmost column of the fusion. - """ - if self._second_cell is not None: - return self._second_cell - if not self._pre_check(): - raise RuntimeError( - "Pre-check failed. No component fusion " "possible and no second cell" - ) - assert self._second_cell is not None - return self._second_cell - - def has_crossing_len2_ob(self) -> bool: - """ - Return True if the tiling contains a crossing length 2 obstruction - between `self.first_cell` and `self.second_cell`. - """ - fcell = self.first_cell - scell = self.second_cell - if self._fuse_row: - possible_obs = [ - GriddedPerm((0, 1), (fcell, scell)), - GriddedPerm((1, 0), (scell, fcell)), - ] - else: - possible_obs = [ - GriddedPerm((0, 1), (fcell, scell)), - GriddedPerm((1, 0), (fcell, scell)), - ] - return any(ob in possible_obs for ob in self._tiling.obstructions) - - def is_crossing_len2(self, gp: GriddedPerm) -> bool: - """ - Return True if the gridded permutation `gp` is a length 2 obstruction - crossing between the first and second cell. - """ - return ( - len(gp) == 2 - and gp.occupies(self.first_cell) - and gp.occupies(self.second_cell) - ) - - @property - def obstruction_fuse_counter(self) -> Counter[GriddedPerm]: - """ - Counter of multiplicities of fused obstructions. - - Crossing length 2 obstructions between first cell and second cell - are ignored. - """ - if self._obstruction_fuse_counter is not None: - return self._obstruction_fuse_counter - obs = (ob for ob in self._tiling.obstructions if not self.is_crossing_len2(ob)) - fuse_counter = self._fuse_counter(obs) - self._obstruction_fuse_counter = fuse_counter - return self._obstruction_fuse_counter - - def obstructions_to_add(self) -> Iterator[GriddedPerm]: - """ - Iterator over all the obstructions obtained by fusing obstructions of - the tiling and then unfusing it in all possible ways. Crossing length 2 - obstructions between first cell and second cell are not processed. - """ - return chain.from_iterable( - self.unfuse_gridded_perm(ob) for ob in self.obstruction_fuse_counter - ) - - def _can_fuse_assumption( - self, assumption: TrackingAssumption, fuse_counter: Counter[GriddedPerm] - ) -> bool: - """ - Return True if an assumption can be fused. That is, prefusion, the gps - are all contained entirely on the left of the fusion region, entirely - on the right, or split in every possible way. - """ - if not isinstance(assumption, ComponentAssumption): - return self.is_left_sided_assumption( - assumption - ) and self.is_right_sided_assumption(assumption) - return self._can_fuse_set_of_gridded_perms(fuse_counter) or ( - all(count == 1 for gp, count in fuse_counter.items()) - and self._is_one_sided_assumption(assumption) - ) - - def _can_fuse_set_of_gridded_perms( - self, fuse_counter: Counter[GriddedPerm] - ) -> bool: - raise NotImplementedError - - def _is_valid_count(self, count: int, gp: GriddedPerm) -> bool: - raise NotImplementedError - - def fusable(self) -> bool: - """ - Return True if adjacent rows can be viewed as one row where you draw a - horizontal line through the components. - """ - if not self._pre_check() or not self.has_crossing_len2_ob(): - return False - new_tiling = self._tiling.add_obstructions(self.obstructions_to_add()) - - return self._tiling == new_tiling and self._check_isolation_level() - - def new_assumption(self) -> TrackingAssumption: - """ - Return the assumption that needs to be counted in order to enumerate. - """ - fcell = self.first_cell - scell = self.second_cell - gps = (GriddedPerm.single_cell((0,), fcell),) - if self._fuse_row: - sum_ob = GriddedPerm((1, 0), (scell, fcell)) - else: - sum_ob = GriddedPerm((1, 0), (fcell, scell)) - if sum_ob in self._tiling.obstructions: - return SumComponentAssumption(gps) - return SkewComponentAssumption(gps) - - def __str__(self) -> str: - s = "ComponentFusion Algorithm for:\n" - s += str(self._tiling) - return s diff --git a/tilings/algorithms/general_fusion.py b/tilings/algorithms/general_fusion.py index 2baf6362..cea092dd 100644 --- a/tilings/algorithms/general_fusion.py +++ b/tilings/algorithms/general_fusion.py @@ -1,3 +1,4 @@ +# pylint: skip-file from itertools import chain from typing import Counter, Dict, Iterator, List, Optional diff --git a/tilings/algorithms/requirement_placement.py b/tilings/algorithms/requirement_placement.py index 6d1166f2..6fdc90f7 100644 --- a/tilings/algorithms/requirement_placement.py +++ b/tilings/algorithms/requirement_placement.py @@ -251,8 +251,7 @@ def stretched_parameters(self, cell: Cell) -> List[ParameterCounter]: """ if self._tiling.parameters: raise NotImplementedError - else: - return [] + return [] def _stretched_obstructions_requirements_and_parameters( self, cell: Cell diff --git a/tilings/map.py b/tilings/map.py index 4e71d599..8a00935a 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -12,8 +12,8 @@ class CellMap: - def __init__(self, map: Dict[Cell, Cell]) -> None: - self._map = map + def __init__(self, cell_map: Dict[Cell, Cell]) -> None: + self._map = cell_map @classmethod def identity(cls, dimensions: Tuple[int, int]) -> "CellMap": @@ -192,6 +192,12 @@ def __init__( self._row_map = row_map self._col_map = col_map self._is_identity = is_identity + super().__init__( + { + cell: self.map_cell(cell) + for cell in itertools.product(self._col_map, self._row_map) + } + ) @classmethod def identity(cls, dimensions: Tuple[int, int]) -> "RowColMap": diff --git a/tilings/strategies/__init__.py b/tilings/strategies/__init__.py index bb256398..171f3a85 100644 --- a/tilings/strategies/__init__.py +++ b/tilings/strategies/__init__.py @@ -6,7 +6,7 @@ SubclassVerificationFactory, ) from .factor import FactorFactory -from .fusion import ComponentFusionFactory, FusionFactory +from .fusion import FusionFactory from .obstruction_inferral import ( EmptyCellInferralFactory, ObstructionInferralFactory, @@ -65,7 +65,6 @@ "PatternPlacementFactory", "SlidingFactory", # Fusion - "ComponentFusionFactory", "FusionFactory", # Inferral "EmptyCellInferralFactory", diff --git a/tilings/strategies/assumption_splitting.py b/tilings/strategies/assumption_splitting.py index 38ee7919..b7235144 100644 --- a/tilings/strategies/assumption_splitting.py +++ b/tilings/strategies/assumption_splitting.py @@ -189,10 +189,6 @@ def decomposition_function(self, comb_class: Tiling) -> Optional[Tuple[Tiling]]: def _split_assumption( self, assumption: TrackingAssumption, components: Tuple[Set[Cell], ...] ) -> List[TrackingAssumption]: - if isinstance(assumption, SkewComponentAssumption): - return self._split_skew_assumption(assumption) - if isinstance(assumption, SumComponentAssumption): - return self._split_sum_assumption(assumption) return self._split_tracking_assumption(assumption, components) @staticmethod diff --git a/tilings/strategies/fusion/__init__.py b/tilings/strategies/fusion/__init__.py index ff2ec87b..a3f45efa 100644 --- a/tilings/strategies/fusion/__init__.py +++ b/tilings/strategies/fusion/__init__.py @@ -1,10 +1,7 @@ -from .component import ComponentFusionFactory, ComponentFusionStrategy from .constructor import FusionConstructor from .fusion import FusionFactory, FusionRule, FusionStrategy __all__ = [ - "ComponentFusionFactory", - "ComponentFusionStrategy", "FusionFactory", "FusionStrategy", "FusionRule", diff --git a/tilings/strategies/fusion/component.py b/tilings/strategies/fusion/component.py deleted file mode 100644 index 02e48945..00000000 --- a/tilings/strategies/fusion/component.py +++ /dev/null @@ -1,87 +0,0 @@ -from typing import Iterator, Optional, Tuple - -from comb_spec_searcher import StrategyFactory -from comb_spec_searcher.strategies import Rule -from tilings import GriddedPerm, Tiling -from tilings.algorithms import ComponentFusion, Fusion - -from .fusion import FusionStrategy - - -class ComponentFusionStrategy(FusionStrategy): - def fusion_algorithm(self, tiling: Tiling) -> Fusion: - return ComponentFusion( - tiling, row_idx=self.row_idx, col_idx=self.col_idx, tracked=self.tracked - ) - - def formal_step(self) -> str: - fusing = "rows" if self.row_idx is not None else "columns" - idx = self.row_idx if self.row_idx is not None else self.col_idx - return "component fuse {} {} and {}".format(fusing, idx, idx + 1) - - def backward_map( - self, - comb_class: Tiling, - objs: Tuple[Optional[GriddedPerm], ...], - children: Optional[Tuple[Tiling, ...]] = None, - left_points: Optional[int] = None, - ) -> Iterator[GriddedPerm]: - """ - The backward direction of the underlying bijection used for object - generation and sampling. - """ - raise NotImplementedError - - -class ComponentFusionFactory(StrategyFactory[Tiling]): - def __init__(self, tracked: bool = False, isolation_level: Optional[str] = None): - self.tracked = tracked - self.isolation_level = isolation_level - - def __call__(self, comb_class: Tiling) -> Iterator[Rule]: - if comb_class.requirements: - return - cols, rows = comb_class.dimensions - for row_idx in range(rows - 1): - algo = ComponentFusion( - comb_class, - row_idx=row_idx, - tracked=self.tracked, - isolation_level=self.isolation_level, - ) - if algo.fusable(): - fused_tiling = algo.fused_tiling() - yield ComponentFusionStrategy(row_idx=row_idx, tracked=self.tracked)( - comb_class, (fused_tiling,) - ) - for col_idx in range(cols - 1): - algo = ComponentFusion( - comb_class, - col_idx=col_idx, - tracked=self.tracked, - isolation_level=self.isolation_level, - ) - if algo.fusable(): - fused_tiling = algo.fused_tiling() - yield ComponentFusionStrategy(col_idx=col_idx, tracked=self.tracked)( - comb_class, (fused_tiling,) - ) - - def __str__(self) -> str: - return f"{'tracked ' if self.tracked else ''}component fusion" - - def __repr__(self) -> str: - return ( - self.__class__.__name__ - + f"(tracked={self.tracked}, isolation_level={self.isolation_level})" - ) - - def to_jsonable(self) -> dict: - d: dict = super().to_jsonable() - d["tracked"] = self.tracked - d["isolation_level"] = self.isolation_level - return d - - @classmethod - def from_dict(cls, d: dict) -> "ComponentFusionFactory": - return cls(**d) diff --git a/tilings/strategies/verification.py b/tilings/strategies/verification.py index 41f90608..9ef1d680 100644 --- a/tilings/strategies/verification.py +++ b/tilings/strategies/verification.py @@ -125,10 +125,6 @@ def __repr__(self) -> str: class OneByOneVerificationStrategy(BasisAwareVerificationStrategy): @staticmethod def pack(comb_class: Tiling) -> StrategyPack: - if any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) # pylint: disable=import-outside-toplevel from tilings.tilescope import TileScopePack @@ -178,9 +174,6 @@ def pack(comb_class: Tiling) -> StrategyPack: def verified(self, comb_class: Tiling) -> bool: return comb_class.dimensions == (1, 1) and ( frozenset(ob.patt for ob in comb_class.obstructions) not in self.symmetries - or any( - isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions - ) ) def get_genf( @@ -299,10 +292,6 @@ class LocallyFactorableVerificationStrategy(BasisAwareVerificationStrategy): """ def pack(self, comb_class: Tiling) -> StrategyPack: - if any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) return StrategyPack( name="LocallyFactorable", initial_strats=[FactorFactory(), RequirementCorroborationFactory()], @@ -321,10 +310,6 @@ def pack(self, comb_class: Tiling) -> StrategyPack: @staticmethod def _pack_for_shift(comb_class: Tiling) -> StrategyPack: - if any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) return StrategyPack( name="LocallyFactorable", initial_strats=[FactorFactory(), RequirementCorroborationFactory()], @@ -452,13 +437,6 @@ def pack(self, comb_class: Tiling) -> StrategyPack: pass if self.no_factors: raise InvalidOperationError("Cannot get a simpler specification") - if ( - any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions) - and len(comb_class.find_factors()) == 1 - ): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) return StrategyPack( initial_strats=[FactorFactory()], inferral_strats=[], @@ -536,10 +514,6 @@ def __init__(self, ignore_parent: bool = True): super().__init__(ignore_parent=ignore_parent) def pack(self, comb_class: Tiling) -> StrategyPack: - if any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) # pylint: disable=import-outside-toplevel from tilings.strategy_pack import TileScopePack @@ -612,10 +586,6 @@ def __init__(self, ignore_parent: bool = True, no_factors: bool = True): super().__init__(ignore_parent=ignore_parent) def pack(self, comb_class: Tiling) -> StrategyPack: - if any(isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions): - raise InvalidOperationError( - "Can't find generating function with component assumption." - ) try: return InsertionEncodingVerificationStrategy().pack(comb_class) except StrategyDoesNotApply: diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index 751076e8..6ecadba9 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -157,31 +157,17 @@ def make_fusion( """ pack = self if component: - pack = pack.add_initial( - strat.ComponentFusionFactory( - tracked=tracked, isolation_level=isolation_level - ), - "{}_component_fusion{}".format( - "tracked" if tracked else "untracked", - "" if isolation_level is None else "_" + isolation_level, - ), - apply_first=apply_first, - ) - else: - pack = pack.add_initial( - strat.FusionFactory(tracked=tracked, isolation_level=isolation_level), - "{}_fusion{}".format( - "tracked" if tracked else "untracked", - "" if isolation_level is None else "_" + isolation_level, - ), - apply_first=apply_first, - ) + raise NotImplementedError("Update to use generalised fusion.") + pack = pack.add_initial( + strat.FusionFactory(tracked=tracked, isolation_level=isolation_level), + "{}_fusion{}".format( + "tracked" if tracked else "untracked", + "" if isolation_level is None else "_" + isolation_level, + ), + apply_first=apply_first, + ) if tracked: pack = pack.add_initial(strat.AddAssumptionFactory(), apply_first=True) - if component: - pack = pack.add_initial( - strat.DetectComponentsStrategy(ignore_parent=True), apply_first=True - ) pack = pack.add_initial( strat.RearrangeAssumptionFactory(), apply_first=True ) diff --git a/tilings/tiling.py b/tilings/tiling.py index e8749155..2ea6d60e 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -28,7 +28,6 @@ from .algorithms import ( AllObstructionInferral, - ComponentFusion, EmptyCellInferral, Factor, FactorWithInterleaving, @@ -1025,7 +1024,7 @@ def component_fusion(self, row=None, col=None): If `row` is not `None` then `row` and `row+1` are fused together. If `col` is not `None` then `col` and `col+1` are fused together. """ - return self._fusion(row, col, ComponentFusion) + raise NotImplementedError("Update to use general fusion algorithm.") def sub_tiling( self, @@ -1257,10 +1256,10 @@ def _handle_html_parameter(self, result: List[str], style) -> List[str]: # display stripes background_image = "background-image: linear-gradient(180deg" stripe_size = 24 // len(has_param[index]) - for i, color in enumerate(has_param[index]): + for idx, color in enumerate(has_param[index]): background_image += f""", - {color} {i*stripe_size}px, - {color} {(i+1)*stripe_size}px""" + {color} {idx*stripe_size}px, + {color} {(idx+1)*stripe_size}px""" background_image += ");" result[index] = f'' return result @@ -1351,12 +1350,14 @@ def get_parameter(self, parameter: str) -> ParameterCounter: idx = parameter.split("_")[1] return self.parameters[int(idx)] - def get_minimum_value(self, param_name: str) -> int: + def get_minimum_value(self, parameter: str) -> int: """ Return the minimum value that can be taken by the parameter. """ - parameter = self.get_parameter(param_name) - return min(parameter.get_value(gp) for gp in self.minimal_gridded_perms()) + actual_parameter = self.get_parameter(parameter) + return min( + actual_parameter.get_value(gp) for gp in self.minimal_gridded_perms() + ) def maximum_length_of_minimum_gridded_perm(self) -> int: """Returns the maximum length of the minimum gridded permutation that From b4cbc9496a5eaa45036d759c973812c31f5ea83d Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Wed, 27 Oct 2021 13:29:30 +0000 Subject: [PATCH 12/41] updated pp to use multiplex and rowcolmap (#365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updated pp to use multiplex and rowcolmap * update forced obs/reqs to use multiplex map * update mapped assumption to use parameters * add a domain method to CellMap and RowColMap * handling special case of the empty perm tiling * fix map parameters to apply childs forward map * adding a couple tests * no need to redefine domain on row col map now * add multiplex map test, only init once while placing * enabling test for pp strategy * assumption -> parameter in a couple of places * add a multiplex map class * use already_minimized_obs for point placements * fix tests to not stretch obs into already empty cells in line with updated multiplex map * smarter pre-image gp that only create good gp * fix mypy and pylint * include forced ob when placing point Co-authored-by: Émile Nadeau --- mypy.ini | 3 + .../algorithms/test_requirement_placements.py | 264 +------------ tests/strategies/test_point_placements.py | 53 ++- tests/test_map.py | 46 +++ tests/test_tiling_with_preimage.py | 29 ++ tilings/algorithms/requirement_placement.py | 370 ++++++++++-------- tilings/map.py | 69 +++- tilings/parameter_counter.py | 4 +- tilings/strategies/factor.py | 11 +- tilings/strategies/requirement_placement.py | 44 ++- tilings/strategies/verification.py | 14 +- tilings/tiling.py | 6 +- tox.ini | 2 +- 13 files changed, 452 insertions(+), 463 deletions(-) diff --git a/mypy.ini b/mypy.ini index 6d6c48d6..fb2e53f8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -28,3 +28,6 @@ ignore_errors = True [mypy-tilings.strategies.row_and_col_separation] ignore_errors = False + +[mypy-tilings.strategies.requirement_placement] +ignore_errors = False \ No newline at end of file diff --git a/tests/algorithms/test_requirement_placements.py b/tests/algorithms/test_requirement_placements.py index 2498f5eb..a2cf246e 100644 --- a/tests/algorithms/test_requirement_placements.py +++ b/tests/algorithms/test_requirement_placements.py @@ -1,10 +1,9 @@ -from itertools import chain - import pytest from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST from tilings import GriddedPerm, Tiling from tilings.algorithms import RequirementPlacement +from tilings.map import RowColMap # ------------------------------------------------ # Fixture and utility @@ -155,122 +154,27 @@ def test_empty_col(placement1): assert placement1.empty_col(1) == t -def test_point_translation(gp1, placement1, placement1owncol, placement1ownrow): - assert placement1._point_translation(gp1, 2, (0, 3)) == (3, 1) - assert placement1._point_translation(gp1, 2, (1, 2)) == (3, 3) - assert placement1._point_translation(gp1, 2, (2, 2)) == (3, 3) - assert placement1._point_translation(gp1, 2, (3, 0)) == (1, 3) - assert placement1._point_translation(gp1, 2, (4, 4)) == (1, 1) - - assert placement1owncol._point_translation(gp1, 2, (0, 3)) == (3, 1) - assert placement1owncol._point_translation(gp1, 2, (1, 2)) == (3, 1) - assert placement1owncol._point_translation(gp1, 2, (2, 2)) == (3, 1) - assert placement1owncol._point_translation(gp1, 2, (3, 0)) == (1, 1) - assert placement1owncol._point_translation(gp1, 2, (4, 4)) == (1, 1) - - assert placement1ownrow._point_translation(gp1, 2, (0, 3)) == (1, 1) - assert placement1ownrow._point_translation(gp1, 2, (1, 2)) == (1, 3) - assert placement1ownrow._point_translation(gp1, 2, (2, 2)) == (1, 3) - assert placement1ownrow._point_translation(gp1, 2, (3, 0)) == (1, 3) - assert placement1ownrow._point_translation(gp1, 2, (4, 4)) == (1, 1) +def test_multiplex_map(placement1, placement1owncol, placement1ownrow): + width = 2 + height = 3 + cell = (1, 1) + row_map = {0: 0, 1: 1, 2: 1, 3: 1, 4: 2} + col_map = {0: 0, 1: 1, 2: 1, 3: 1} + row_col_map = RowColMap(row_map, col_map) + print(row_col_map) + print(placement1.multiplex_map(width, height, cell)) + assert placement1.multiplex_map(width, height, cell) == row_col_map -def test_gridded_perm_translation(gp1, placement1, placement1owncol, placement1ownrow): - assert placement1._gridded_perm_translation(gp1, (0, 3)) == GriddedPerm( - (3, 1, 2, 0, 4), ((2, 3), (2, 0), (3, 1), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation(gp1, (1, 1)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (2, 2), (3, 3), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation(gp1, (2, 2)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 0), (3, 3), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation(gp1, (3, 0)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 2), (1, 3), (3, 2), (3, 3)) - ) - assert placement1._gridded_perm_translation(gp1, (4, 4)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (3, 3)) - ) - assert placement1owncol._gridded_perm_translation(gp1, (0, 3)) == GriddedPerm( - (3, 1, 2, 0, 4), ((2, 1), (2, 0), (3, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation(gp1, (1, 1)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (2, 0), (3, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation(gp1, (2, 2)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (3, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation(gp1, (3, 0)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation(gp1, (4, 4)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (3, 1)) - ) - assert placement1ownrow._gridded_perm_translation(gp1, (0, 3)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 1), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation(gp1, (1, 1)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 2), (1, 3), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation(gp1, (2, 2)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 3), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation(gp1, (3, 0)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 2), (1, 3), (1, 2), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation(gp1, (4, 4)) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 3)) - ) - + row_map = {0: 0, 1: 1, 2: 2} + col_map = {0: 0, 1: 1, 2: 1, 3: 1} + row_col_map = RowColMap(row_map, col_map) + assert placement1owncol.multiplex_map(width, height, cell) == row_col_map -def test_gridded_perm_translation_with_point( - gp1, placement1, placement1owncol, placement1ownrow -): - assert placement1._gridded_perm_translation_with_point(gp1, 0) == GriddedPerm( - (3, 1, 2, 0, 4), ((1, 2), (2, 0), (3, 1), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation_with_point(gp1, 1) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (1, 1), (3, 3), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation_with_point(gp1, 2) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 0), (2, 2), (3, 0), (3, 3)) - ) - assert placement1._gridded_perm_translation_with_point(gp1, 3) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 2), (1, 3), (2, 1), (3, 3)) - ) - assert placement1._gridded_perm_translation_with_point(gp1, 4) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (2, 2)) - ) - assert placement1ownrow._gridded_perm_translation_with_point(gp1, 0) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 2), (0, 0), (1, 1), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation_with_point(gp1, 1) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 1), (1, 3), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation_with_point(gp1, 2) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 2), (1, 0), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation_with_point(gp1, 3) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 3), (0, 2), (1, 3), (1, 1), (1, 3)) - ) - assert placement1ownrow._gridded_perm_translation_with_point(gp1, 4) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 2)) - ) - assert placement1owncol._gridded_perm_translation_with_point(gp1, 0) == GriddedPerm( - (3, 1, 2, 0, 4), ((1, 1), (2, 0), (3, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation_with_point(gp1, 1) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (1, 0), (3, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation_with_point(gp1, 2) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (2, 1), (3, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation_with_point(gp1, 3) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (2, 0), (3, 1)) - ) - assert placement1owncol._gridded_perm_translation_with_point(gp1, 4) == GriddedPerm( - (3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (2, 1)) - ) + row_map = {0: 0, 1: 1, 2: 1, 3: 1, 4: 2} + col_map = {0: 0, 1: 1} + row_col_map = RowColMap(row_map, col_map) + assert placement1ownrow.multiplex_map(width, height, cell) == row_col_map def test_placed_cell(placement1, placement1owncol, placement1ownrow): @@ -307,131 +211,6 @@ def test_point_requirements(placement1, placement1owncol, placement1ownrow): ] -def test_stretch_gridded_perm(gp1, placement1, placement1owncol, placement1ownrow): - assert set(placement1._stretch_gridded_perm(gp1, (0, 0))) == set( - [ - GriddedPerm((3, 1, 2, 0, 4), ((2, 3), (2, 2), (3, 3), (3, 2), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((2, 3), (2, 2), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((2, 3), (2, 0), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (2, 2), (3, 3), (3, 2), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (2, 2), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (2, 0), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 2), (3, 3), (3, 2), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 2), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 0), (3, 3), (3, 0), (3, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (1, 1), (3, 3), (3, 0), (3, 3))), - ] - ) - assert set(placement1owncol._stretch_gridded_perm(gp1, (1, 0))) == set( - [ - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (3, 1), (3, 0), (3, 1))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (3, 0), (3, 1))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (3, 1))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 1))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (2, 0), (3, 1))), - ] - ) - assert set(placement1ownrow._stretch_gridded_perm(gp1, (1, 1))) == set( - [ - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 3), (1, 0), (1, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 1), (1, 0), (1, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 1))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 3), (0, 0), (1, 2), (1, 0), (1, 3))), - GriddedPerm((3, 1, 2, 0, 4), ((0, 1), (0, 0), (1, 1), (1, 0), (1, 2))), - ] - ) - - -def test_stretch_gridded_perms(placement1, placement1owncol, placement1ownrow): - gps = [ - GriddedPerm((0, 1), [(0, 0), (1, 1)]), - GriddedPerm((0, 1), [(1, 1), (2, 2)]), - ] - for p in (placement1, placement1ownrow, placement1owncol): - assert set(p._stretch_gridded_perms(gps, (1, 1))) == set( - chain.from_iterable(p._stretch_gridded_perm(gp, (1, 1)) for gp in gps) - ) - - -def test_stretched_obstructions(placement1, placement1owncol, placement1ownrow): - orig_obs = placement1._tiling.obstructions - assert sorted(placement1.stretched_obstructions((1, 1))) == sorted( - placement1._stretch_gridded_perms(orig_obs, (1, 1)) - ) - assert sorted(placement1owncol.stretched_obstructions((1, 1))) == sorted( - placement1owncol._stretch_gridded_perms(orig_obs, (1, 1)) - ) - assert sorted(placement1ownrow.stretched_obstructions((1, 1))) == sorted( - placement1ownrow._stretch_gridded_perms(orig_obs, (1, 1)) - ) - - -def test_stretched_requirements(placement1, placement1owncol, placement1ownrow): - orig_reqs = placement1._tiling.requirements - assert sorted(placement1.stretched_requirements((1, 1))) == sorted( - placement1._stretch_gridded_perms(orig_reqs, (1, 1)) - ) - orig_reqs = placement1owncol._tiling.requirements - assert sorted(placement1owncol.stretched_requirements((1, 1))) == sorted( - placement1owncol._stretch_gridded_perms(orig_reqs, (1, 1)) - ) - orig_reqs = placement1ownrow._tiling.requirements - assert sorted(placement1ownrow.stretched_requirements((1, 1))) == sorted( - placement1ownrow._stretch_gridded_perms(orig_reqs, (1, 1)) - ) - - -def test_stretched_obstructions_and_parameters( - placement1, placement1owncol, placement1ownrow -): - obs, reqs, _ = placement1._stretched_obstructions_requirements_and_parameters( - (1, 1) - ) - assert set(obs) == set( - placement1.stretched_obstructions((1, 1)) - + [ - GriddedPerm.single_cell((0, 1), (2, 2)), - GriddedPerm.single_cell((1, 0), (2, 2)), - ] - ) - assert sorted(reqs) == sorted( - placement1.stretched_requirements((1, 1)) + [[GriddedPerm((0,), ((2, 2),))]] - ) - ( - obs, - reqs, - _, - ) = placement1ownrow._stretched_obstructions_requirements_and_parameters((1, 1)) - assert set(obs) == set( - placement1ownrow.stretched_obstructions((1, 1)) - + [ - GriddedPerm.single_cell((0, 1), (1, 2)), - GriddedPerm.single_cell((1, 0), (1, 2)), - ] - ) - assert sorted(reqs) == sorted( - placement1ownrow.stretched_requirements((1, 1)) - + [[GriddedPerm((0,), ((1, 2),))]] - ) - ( - obs, - reqs, - _, - ) = placement1owncol._stretched_obstructions_requirements_and_parameters((1, 1)) - assert set(obs) == set( - placement1owncol.stretched_obstructions((1, 1)) - + [ - GriddedPerm.single_cell((0, 1), (2, 1)), - GriddedPerm.single_cell((1, 0), (2, 1)), - ] - ) - assert sorted(reqs) == sorted( - placement1owncol.stretched_requirements((1, 1)) - + [[GriddedPerm((0,), ((2, 1),))]] - ) - - def farther(placement1): assert placement1._farther((0, 0), (2, 0), DIR_EAST) is False assert placement1._farther((0, 0), (2, 0), DIR_NORTH) is False @@ -498,13 +277,12 @@ def test_forced_obstructions_from_patt( ) -def test_forced_obstructions_from_list( - gp1, placement1, placement1owncol, placement1ownrow -): +def test_forced_obstructions_from_list(placement1, placement1owncol, placement1ownrow): req_list_row = [ GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),)), ] + print(placement1._tiling) assert set( placement1.forced_obstructions_from_requirement( req_list_row, (0, 0), (0, 0), DIR_NORTH diff --git a/tests/strategies/test_point_placements.py b/tests/strategies/test_point_placements.py index dac0bcbe..0bf58d3e 100644 --- a/tests/strategies/test_point_placements.py +++ b/tests/strategies/test_point_placements.py @@ -4,7 +4,8 @@ from comb_spec_searcher.strategies import Rule from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS from tilings import GriddedPerm, Tiling -from tilings.assumptions import TrackingAssumption +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategies import ( AllPlacementsFactory, PatternPlacementFactory, @@ -1334,7 +1335,6 @@ def test_reverse_rule_non_empty_children(): GriddedPerm((1, 0), ((2, 6), (2, 0))), ), ), - assumptions=(), ) rule = strategy(tiling) eqv_rule = rule.to_equivalence_rule() @@ -1371,13 +1371,22 @@ def test_multiple_parent_parameters_to_same_child_parameter(): GriddedPerm((0, 2, 3, 1), ((0, 0), (0, 0), (3, 0), (3, 0))), ), requirements=(), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((2, 0),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((2, 0),)), GriddedPerm((0,), ((3, 0),))) - ), - ), ) + + row_map = {0: 0} + col_map1 = {0: 0, 1: 1, 2: 2, 3: 2, 4: 3} + col_map2 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 3} + + row_col_map1 = RowColMap(row_map, col_map1) + row_col_map2 = RowColMap(row_map, col_map2) + + preimage1 = PreimageCounter(row_col_map1.preimage_tiling(tiling), row_col_map1) + preimage2 = PreimageCounter(row_col_map2.preimage_tiling(tiling), row_col_map2) + + param1 = ParameterCounter([preimage1]) + param2 = ParameterCounter([preimage1, preimage2]) + + tiling = tiling.add_parameters([param1, param2]) strategy = RequirementPlacementStrategy( gps=( GriddedPerm((0,), ((1, 0),)), @@ -1395,3 +1404,31 @@ def test_multiple_parent_parameters_to_same_child_parameter(): rule = strategy(tiling) for i in range(6): rule.sanity_check(i) + + +def test_place_with_params(): + tiling = Tiling( + obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))),), + requirements=(), + parameters=[ + ParameterCounter( + [ + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0, 1: 0}, {0: 0}), + ) + ] + ) + ], + ) + for rule in AllPlacementsFactory()(tiling): + for i in range(5): + assert rule.sanity_check(i) diff --git a/tests/test_map.py b/tests/test_map.py index 8aadc49d..33ca7a0c 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -62,6 +62,52 @@ def test_preimage_gp(double_all_map): ] +def test_preimage_gp_crossing_map(): + cross_col = RowColMap(col_map={0: 1, 1: 0}, row_map={0: 0}) + assert sorted(cross_col.preimage_gp(GriddedPerm((0, 1), ((0, 0),) * 2))) == [ + GriddedPerm((0, 1), ((1, 0),) * 2) + ] + assert sorted(cross_col.preimage_gp(GriddedPerm((0, 1), ((1, 0),) * 2))) == [ + GriddedPerm((0, 1), ((0, 0),) * 2) + ] + assert ( + sorted( + cross_col.preimage_gp( + GriddedPerm( + (0, 1), + ( + (0, 0), + (1, 0), + ), + ) + ) + ) + == [] + ) + + cross_row = RowColMap(col_map={0: 0}, row_map={0: 1, 1: 0}) + assert sorted(cross_row.preimage_gp(GriddedPerm((0, 1), ((0, 0),) * 2))) == [ + GriddedPerm((0, 1), ((0, 1),) * 2) + ] + assert sorted(cross_row.preimage_gp(GriddedPerm((0, 1), ((0, 1),) * 2))) == [ + GriddedPerm((0, 1), ((0, 0),) * 2) + ] + assert ( + sorted( + cross_row.preimage_gp( + GriddedPerm( + (0, 1), + ( + (0, 0), + (0, 1), + ), + ) + ) + ) + == [] + ) + + def test_preimage_tiling(double_row_map, double_col_map, double_all_map): t1 = Tiling( obstructions=[ diff --git a/tests/test_tiling_with_preimage.py b/tests/test_tiling_with_preimage.py index 55bb24d6..6dfed5cb 100644 --- a/tests/test_tiling_with_preimage.py +++ b/tests/test_tiling_with_preimage.py @@ -122,3 +122,32 @@ def test_insert_point_req_tiling_with_req(normal_fusion_tiling): # insert in cell with two preimages t_base = normal_fusion_tiling.insert_cell((1, 0)) assert t_expected == t_base.insert_cell((0, 0)) + + +def test_tiling_is_empty_perm_tiling(): + tiling = Tiling( + obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))),), + requirements=(), + parameters=[ + ParameterCounter( + [ + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0, 1: 0}, {0: 0}), + ) + ] + ) + ], + ) + empty_cell = tiling.empty_cell((0, 0)) + assert empty_cell.get_terms(0) == {(1,): 1} + for i in range(1, 4): + assert empty_cell.get_terms(i) == {} diff --git a/tilings/algorithms/requirement_placement.py b/tilings/algorithms/requirement_placement.py index 6fdc90f7..2c29cc42 100644 --- a/tilings/algorithms/requirement_placement.py +++ b/tilings/algorithms/requirement_placement.py @@ -1,9 +1,10 @@ -from itertools import chain -from typing import TYPE_CHECKING, Dict, FrozenSet, Iterable, List, Tuple +import itertools +from typing import TYPE_CHECKING, Dict, FrozenSet, Iterable, Iterator, List, Tuple from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS from tilings.griddedperm import GriddedPerm -from tilings.parameter_counter import ParameterCounter +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter if TYPE_CHECKING: from tilings import Tiling @@ -16,6 +17,77 @@ ParamCache = Dict[Cell, List[ParameterCounter]] +class MultiplexMap(RowColMap): + r""" + A special class that maps + + - + - + - + + | A | | A | \ + + - + - + - + + - + + | | o | | - | A | + + - + - + - + + - + + | A | | A | / + + - + - + - + + where the preimage does not place points in the empty cells. + """ + + def __init__( + self, width: int, height: int, cell: Cell, own_col: bool, own_row: bool + ): + x, y = cell + self.cell = cell + col_map = self.get_row_map(x, width, own_col, False) + row_map = self.get_row_map(y, height, own_row, False) + super().__init__(row_map, col_map) + # Create the partial map that only maps from the corners. + # This allows for faster preimage computation. + self.own_col = own_col + self.own_row = own_row + partial_col_map = self.get_row_map(x, width, own_col, True) + partial_row_map = self.get_row_map(y, height, own_row, True) + self.partial_map = RowColMap(partial_row_map, partial_col_map) + + @staticmethod + def get_row_map( + row: int, height: int, own_row: bool, partial: bool + ) -> Dict[int, int]: + row_map = dict() + for j in range(height): + ys = ( + [j] + if j < row or not own_row + else ([j, j + 2] if partial else [j, j + 1, j + 2]) + if j == row + else [j + 2] + ) + for b in ys: + row_map[b] = j + return row_map + + def preimage_gp(self, gp: "GriddedPerm") -> Iterator["GriddedPerm"]: + """ + Returns all the preimages of the given gridded permutation. + + Gridded permutations that are contradictory are filtered out. + """ + yield from self.partial_map.preimage_gp(gp) + for (idx, val), cell in zip(enumerate(gp.patt), gp.pos): + if cell == self.cell: + new_pos: List[Cell] = [] + for (a, b), (c, d) in zip(enumerate(gp.patt), gp.pos): + if self.own_col: + if a == idx: + c += 1 + elif a > idx: + c += 2 + if self.own_row: + if b == val: + d += 1 + elif b > val: + d += 2 + new_pos.append((c, d)) + yield GriddedPerm(gp.patt, new_pos) + + class RequirementPlacement: """ The requirement placement container class. @@ -50,9 +122,6 @@ def __init__( self._point_col_cells = self._tiling_point_col_cells() self.own_row = own_row self.own_col = own_col - self._stretched_obstructions_cache: ObsCache = {} - self._stretched_requirements_cache: ReqsCache = {} - self._stretched_parameters_cache: ParamCache = {} if self.own_row and self.own_col: self.directions = frozenset(DIRS) elif self.own_row: @@ -99,67 +168,6 @@ def already_placed( return cell in self._point_col_cells raise Exception("Not placing at all!!") - def _point_translation( - self, gp: GriddedPerm, index: int, placed_cell: Cell - ) -> Cell: - """ - Return the translated position of the cell at the given index. - - The translation assumes that there has been a point placed in the - position (i, j) = placed_cell where this corresponds to the index and - value within the pattern of the gridded permutation gp. - - If the newly placed point is assumed to put on the the new column we - have that the cell is expanded like: - - - - - - | | -> | |o| | - - - - - - meaning that indices to the right of i are shifted by 2. - Similarly, for new rows we have - - - | | - - - - | | -> |o| - - - - | | - - - meaning that values above j are shifted by 2. - """ - x, y = gp.pos[index] - return ( - x + 2 if self.own_col and index >= placed_cell[0] else x, - y + 2 if (self.own_row and gp.patt[index] >= placed_cell[1]) else y, - ) - - def _gridded_perm_translation( - self, gp: GriddedPerm, placed_cell: Cell - ) -> GriddedPerm: - """ - Return the gridded permutation with all of the cells translated - assuming that the point was placed at placed cell - """ - newpos = [ - self._point_translation(gp, index, placed_cell) for index in range(len(gp)) - ] - return gp.__class__(gp.patt, newpos) - - def _gridded_perm_translation_with_point( - self, gp: GriddedPerm, point_index: int - ) -> GriddedPerm: - """ - Return the stretched gridded permutation obtained when the point at - point_index in gp is placed. - """ - # TODO: to prepare for intervals consider all ways of drawing a - # rectangle around point in cell. - new_pos = [ - self._point_translation(gp, i, (point_index, gp.patt[point_index])) - if i != point_index - else self._placed_cell(gp.pos[point_index]) - for i in range(len(gp)) - ] - return gp.__class__(gp.patt, new_pos) - def _placed_cell(self, cell: Cell) -> Cell: """ Return the cell in which the point will be added in the placed tiling. @@ -189,84 +197,6 @@ def _point_requirements(self, cell: Cell) -> List[ListRequirement]: placed_cell = self._placed_cell(cell) return [[GriddedPerm((0,), (placed_cell,))]] - def _stretch_gridded_perm( - self, gp: GriddedPerm, cell: Cell - ) -> Iterable[GriddedPerm]: - """ - Return all of the possible ways that a gridded permutation can be - stretched assuming that a point is placed into the given cell. - """ - mindex, maxdex, minval, maxval = gp.get_bounding_box(cell) - if not self.own_col: - maxdex = mindex - elif not self.own_row: - maxval = minval - res = [ - self._gridded_perm_translation(gp, (i, j)) - for i in range(mindex, maxdex + 1) - for j in range(minval, maxval + 1) - ] - for i in gp.points_in_cell(cell): - res.append(self._gridded_perm_translation_with_point(gp, i)) - return res - - def _stretch_gridded_perms( - self, gps: Iterable[GriddedPerm], cell: Cell - ) -> List[GriddedPerm]: - """ - Return all stretched gridded permuations for an iterable of gridded - permutations, assuming a point is placed in the given cell. - """ - return list( - chain.from_iterable(self._stretch_gridded_perm(gp, cell) for gp in gps) - ) - - def stretched_obstructions(self, cell: Cell) -> List[GriddedPerm]: - """ - Return all of the stretched obstructions that are created if placing a - point in the given cell. - """ - if cell not in self._stretched_obstructions_cache: - self._stretched_obstructions_cache[cell] = self._stretch_gridded_perms( - self._tiling.obstructions, cell - ) - return self._stretched_obstructions_cache[cell] - - def stretched_requirements(self, cell: Cell) -> List[ListRequirement]: - """ - Return all of the stretched requirements that are created if placing a - point in the given cell. - """ - if cell not in self._stretched_requirements_cache: - self._stretched_requirements_cache[cell] = [ - self._stretch_gridded_perms(req_list, cell) - for req_list in self._tiling.requirements - ] - return self._stretched_requirements_cache[cell] - - def stretched_parameters(self, cell: Cell) -> List[ParameterCounter]: - """ - Return all of the stretched parameters that are created if placing a - point in the given cell. - """ - if self._tiling.parameters: - raise NotImplementedError - return [] - - def _stretched_obstructions_requirements_and_parameters( - self, cell: Cell - ) -> Tuple[List[GriddedPerm], List[ListRequirement], List[ParameterCounter]]: - """ - Return all of the stretched obstruction and requirements assuming that - a point is placed in cell. - """ - stretched_obs = self.stretched_obstructions(cell) - stretched_reqs = self.stretched_requirements(cell) - stretched_params = self.stretched_parameters(cell) - point_obs = self._point_obstructions(cell) - point_req = self._point_requirements(cell) - return stretched_obs + point_obs, stretched_reqs + point_req, stretched_params - @staticmethod def _farther(c1: Cell, c2: Cell, direction: Dir) -> bool: """Return True if c1 is farther in the given direction than c2.""" @@ -280,6 +210,30 @@ def _farther(c1: Cell, c2: Cell, direction: Dir) -> bool: return c1[1] < c2[1] raise Exception("Invalid direction") + def empty_row_and_col_obs( + self, cell: Cell, width: int, height: int + ) -> List[GriddedPerm]: + """ + Return the obstructions needed to ensure point is the only on a the row + and/or column assuming that the point was placed in a cell on a tiling + with the given height and width + """ + if self.own_col: + width += 2 + if self.own_row: + height += 2 + res: List[GriddedPerm] = [] + empty_col, empty_row = self._placed_cell(cell) + if self.own_row: + for i in range(width): + if i != empty_col: + res.append(GriddedPerm.point_perm((i, empty_row))) + if self.own_col: + for j in range(height): + if j != empty_row: + res.append(GriddedPerm.point_perm((empty_col, j))) + return res + def forced_obstructions_from_requirement( self, gps: Iterable[GriddedPerm], @@ -296,18 +250,19 @@ def forced_obstructions_from_requirement( from any gridded permutation in gp_list in which the point at idx is farther in the given direction than the placed cell. """ + multiplex_map = self.multiplex_map(*self._tiling.dimensions, cell) placed_cell = self._placed_cell(cell) res = [] for idx, gp in zip(indices, gps): # if cell is farther in the direction than gp[idx], then don't need # to avoid any of the stretched grided perms if not self._farther(cell, gp.pos[idx], direction): - for stretched_gp in self._stretch_gridded_perm(gp, cell): + for stretched_gp in multiplex_map.preimage_gp(gp): if self._farther(stretched_gp.pos[idx], placed_cell, direction): res.append(stretched_gp) return res - def _remaining_requirement_from_requirement( + def remaining_requirement_from_requirement( self, gps: Iterable[GriddedPerm], indices: Iterable[int], cell: Cell ) -> List[GriddedPerm]: """ @@ -319,24 +274,102 @@ def _remaining_requirement_from_requirement( a gridded permutation in gps, such that the point at idx is the placed cell. """ + multiplex_map = self.multiplex_map(*self._tiling.dimensions, cell) placed_cell = self._placed_cell(cell) res = [] for idx, gp in zip(indices, gps): if gp.pos[idx] == cell: - for stretched_gp in self._stretch_gridded_perm(gp, cell): + for stretched_gp in multiplex_map.preimage_gp(gp): if stretched_gp.pos[idx] == placed_cell: res.append(stretched_gp) return res - def place_point_of_gridded_permutation( - self, gp: GriddedPerm, idx: int, direction: Dir - ) -> "Tiling": + def multiplex_map(self, width: int, height: int, cell: Cell) -> RowColMap: + """Return the RowColMap when cell is stretched into a 3x3.""" + # TODO: cache this? + return MultiplexMap(width, height, cell, self.own_col, self.own_row) + + def multiplex_tiling( + self, tiling: "Tiling", cell: Cell + ) -> Tuple[List[GriddedPerm], List[List[GriddedPerm]], List[ParameterCounter]]: """ - Return the tiling where the placed point correspond to the - directionmost (the furtest in the given direction, ex: leftmost point) - occurrence of the idx point in gp. + Return the tiling created by 'multipexing' in cell. + That is stretching the cell to be a 3x3 square. """ - return self.place_point_of_req((gp,), (idx,), direction)[0] + # TODO: cache this? + row_col_map = self.multiplex_map(*tiling.dimensions, cell) + # TODO: should we optimise this to stop adding gps that are in cells we will + # later mark as empty? + obs, reqs = row_col_map.preimage_obstruction_and_requirements( + tiling.remove_parameters() + ) + params = [self.multiplex_parameter(param, cell) for param in tiling.parameters] + return ( + obs + + self.empty_row_and_col_obs(cell, *tiling.dimensions) + + self._point_obstructions(cell), + reqs + self._point_requirements(cell), + params, + ) + + def multiplex_preimage( + self, preimage: PreimageCounter, cell: Cell + ) -> List[PreimageCounter]: + """ + Return the list of preimages whose sum count the number of preimages + when cell is multiplexed into a 3x3. + """ + res: List[PreimageCounter] = [] + width, height = preimage.tiling.dimensions + if self.own_col: + width += 2 + if self.own_row: + height += 2 + for precell in preimage.map.preimage_cell(cell): + preimage_multiplex_map = self.multiplex_map( + *preimage.tiling.dimensions, precell + ) + multiplex_tiling = preimage.tiling.__class__( + *self.multiplex_tiling(preimage.tiling, precell) + ) + col_map = {} + for x in range(width): + shift = ( + 0 + if x <= precell[0] or not self.own_col + else 1 + if x == precell[0] + 1 + else 2 + ) + col_map[x] = ( + preimage.map.map_col(preimage_multiplex_map.map_col(x)) + shift + ) + row_map = {} + for y in range(height): + shift = ( + 0 + if y <= precell[1] or not self.own_row + else 1 + if y == precell[1] + 1 + else 2 + ) + row_map[y] = ( + preimage.map.map_row(preimage_multiplex_map.map_row(y)) + shift + ) + res.append(PreimageCounter(multiplex_tiling, RowColMap(row_map, col_map))) + return res + + def multiplex_parameter( + self, parameter: ParameterCounter, cell: Cell + ) -> ParameterCounter: + """ + Return the parameter when cell has been multiplexed into a 3x3. + """ + return ParameterCounter( + itertools.chain.from_iterable( + self.multiplex_preimage(preimage, cell) for preimage in parameter + ) + ) def place_point_of_req( self, gps: Iterable[GriddedPerm], indices: Iterable[int], direction: Dir @@ -349,27 +382,36 @@ def place_point_of_req( cells = frozenset(gp.pos[idx] for idx, gp in zip(indices, gps)) res = [] for cell in sorted(cells): - stretched = self._stretched_obstructions_requirements_and_parameters(cell) - (obs, reqs, param) = stretched + obs, reqs, params = self.multiplex_tiling(self._tiling, cell) forced_obs = self.forced_obstructions_from_requirement( gps, indices, cell, direction ) - reduced_obs = [o1 for o1 in obs if not any(o2 in o1 for o2 in forced_obs)] - new_obs = reduced_obs + forced_obs - - rem_req = self._remaining_requirement_from_requirement(gps, indices, cell) - + rem_req = self.remaining_requirement_from_requirement(gps, indices, cell) + params = [ + param.add_obstructions_and_requirements(forced_obs, [rem_req]) + for param in params + ] res.append( self._tiling.__class__( - new_obs, + reduced_obs + forced_obs, reqs + [rem_req], - parameters=param, + params, already_minimized_obs=True, ) ) return tuple(res) + def place_point_of_gridded_permutation( + self, gp: GriddedPerm, idx: int, direction: Dir + ) -> "Tiling": + """ + Return the tiling where the placed point correspond to the + directionmost (the furtest in the given direction, ex: leftmost point) + occurrence of the idx point in gp. + """ + return self.place_point_of_req((gp,), (idx,), direction)[0] + def place_point_in_cell(self, cell: Cell, direction: Dir) -> "Tiling": """ Return the tiling in which a point is placed in the given direction and diff --git a/tilings/map.py b/tilings/map.py index 8a00935a..e7cad59f 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -1,5 +1,5 @@ import itertools -from typing import TYPE_CHECKING, Dict, Iterator, Optional, Tuple +from typing import TYPE_CHECKING, Callable, Dict, Iterator, List, Optional, Tuple from tilings.exception import InvalidOperationError from tilings.griddedperm import GriddedPerm @@ -20,6 +20,12 @@ def identity(cls, dimensions: Tuple[int, int]) -> "CellMap": cells = itertools.product(range(dimensions[0]), range(dimensions[1])) return CellMap({c: c for c in cells}) + def domain(self) -> Iterator[Cell]: + """ + Return the domain of the map. + """ + return iter(self._map) + def inverse(self) -> "CellMap": """ Return the inverse map if possible. @@ -104,7 +110,7 @@ def map_preimage_counter( cell_pos_in_col, cell_pos_in_row = dict(), dict() col_split = [0 for _ in range(preimg_counter.tiling.dimensions[0])] row_split = [0 for _ in range(preimg_counter.tiling.dimensions[1])] - for cell in self._map: + for cell in self.domain(): for pre_cell in preimg_counter.map.preimage_cell(cell): col_pos = self.map_cell(cell)[0] - cell[0] row_pos = self.map_cell(cell)[1] - cell[1] @@ -309,18 +315,62 @@ def preimage_cell(self, cell: Cell) -> Iterator[Cell]: col, row = cell return itertools.product(self.preimage_col(col), self.preimage_row(row)) + @staticmethod + def _preimage_gp_col( + gp_cols: Tuple[int, ...], preimage_func: Callable[[int], Iterator[int]] + ) -> Iterator[Tuple[int, ...]]: + """ + Return all the possible sequence of column for a preimage of the gridded + permutation using the given preimage_func. + """ + possible_col = [sorted(preimage_func(col)) for col in gp_cols] + partial_pos: List[int] = [] + partial_pos_indices: List[int] = [] + while True: + # Padding the current solution with the leftmost options + while len(partial_pos) < len(gp_cols): + last_col = partial_pos[-1] if partial_pos else 0 + for new_col_idx, col in enumerate(possible_col[len(partial_pos)]): + if last_col <= col: + partial_pos.append(col) + partial_pos_indices.append(new_col_idx) + break + else: + break + else: + yield tuple(partial_pos) + # increasing the rightmost pos that can be increased. + while partial_pos: + partial_pos.pop() + partial_pos_last_index = partial_pos_indices.pop() + if partial_pos_last_index + 1 < len(possible_col[len(partial_pos)]): + break + else: + break + partial_pos.append( + possible_col[len(partial_pos)][partial_pos_last_index + 1] + ) + partial_pos_indices.append(partial_pos_last_index + 1) + def preimage_gp(self, gp: "GriddedPerm") -> Iterator["GriddedPerm"]: """ Returns all the preimages of the given gridded permutation. Gridded permutations that are contradictory are filtered out. """ - for pos in itertools.product(*(self.preimage_cell(cell) for cell in gp.pos)): - new_gp = gp.__class__(gp.patt, pos) - if not new_gp.contradictory(): - yield new_gp + gp_cols = tuple(col for col, _ in gp.pos) + preimage_col_pos = self._preimage_gp_col(gp_cols, self.preimage_col) + gp_rows = gp.patt.inverse().apply(row for _, row in gp.pos) + preimage_row_pos: Iterator[Tuple[int, ...]] = map( + gp.patt.apply, self._preimage_gp_col(gp_rows, self.preimage_row) + ) + for pos in itertools.product(preimage_col_pos, preimage_row_pos): + new_gp = gp.__class__(gp.patt, zip(*pos)) + yield new_gp - def preimage_tiling(self, tiling: "Tiling") -> "Tiling": + def preimage_obstruction_and_requirements( + self, tiling: "Tiling" + ) -> Tuple[List[GriddedPerm], List[List[GriddedPerm]]]: if tiling.parameters: raise NotImplementedError("Not implemented for tilings with parameter") obs = itertools.chain.from_iterable( @@ -330,7 +380,10 @@ def preimage_tiling(self, tiling: "Tiling") -> "Tiling": itertools.chain.from_iterable(self.preimage_gp(req) for req in req_list) for req_list in tiling.requirements ) - return tiling.__class__(obs, reqs) + return list(obs), list(list(r) for r in reqs) + + def preimage_tiling(self, tiling: "Tiling") -> "Tiling": + return tiling.__class__(*self.preimage_obstruction_and_requirements(tiling)) # Other method def max_row(self) -> int: diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 2669bd72..b327fd27 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -121,7 +121,7 @@ class ParameterCounter: """ def __init__(self, counters: Iterable[PreimageCounter]): - self.counters = tuple(sorted(set(counters))) + self.counters = tuple(sorted(counters)) def active_region(self) -> Iterator[Cell]: """ @@ -182,7 +182,7 @@ def __repr__(self) -> str: return f"{self.__class__.__name__}({self.counters!r})" def __str__(self): - return "".join(map(str, self.counters)) + return "\n".join(map(str, self.counters)) def __iter__(self) -> Iterator[PreimageCounter]: return iter(self.counters) diff --git a/tilings/strategies/factor.py b/tilings/strategies/factor.py index 9692da29..675e358f 100644 --- a/tilings/strategies/factor.py +++ b/tilings/strategies/factor.py @@ -65,15 +65,14 @@ def extra_parameters( if children is None: raise StrategyDoesNotApply("Strategy does not apply") extra_parameters: Tuple[Dict[str, str], ...] = tuple({} for _ in children) - for parent_var, assumption in zip( - comb_class.extra_parameters, comb_class.assumptions + for parent_var, parameter in zip( + comb_class.extra_parameters, comb_class.parameters ): for idx, child in enumerate(children): # TODO: consider skew/sum - new_assumption = child.forward_map.map_assumption(assumption) - if new_assumption.gps: - child_var = child.get_assumption_parameter(new_assumption) - extra_parameters[idx][parent_var] = child_var + new_parameter = child.forward_map.map_param(parameter) + child_var = child.get_parameter_name(new_parameter) + extra_parameters[idx][parent_var] = child_var return extra_parameters def formal_step(self) -> str: diff --git a/tilings/strategies/requirement_placement.py b/tilings/strategies/requirement_placement.py index 04d831ad..815b70b0 100644 --- a/tilings/strategies/requirement_placement.py +++ b/tilings/strategies/requirement_placement.py @@ -90,30 +90,36 @@ def extra_parameters( extra_parameters: Tuple[Dict[str, str], ...] = tuple({} for _ in children) if self.include_empty: child = children[0] - for assumption in comb_class.assumptions: - mapped_assumption = child.forward_map.map_assumption( - assumption - ).avoiding(child.obstructions) - if mapped_assumption.gps: - parent_var = comb_class.get_assumption_parameter(assumption) - child_var = child.get_assumption_parameter(mapped_assumption) - extra_parameters[0][parent_var] = child_var + for parameter in comb_class.parameters: + mapped_parameter = parameter.add_obstructions_and_requirements( + child.obstructions, [] + ).apply_row_col_map(child.forward_map) + parent_var = comb_class.get_parameter_name(parameter) + child_var = child.get_parameter_name(mapped_parameter) + extra_parameters[0][parent_var] = child_var for idx, (cell, child) in enumerate( zip(self._placed_cells, children[1:] if self.include_empty else children) ): - mapped_assumptions = [ - child.forward_map.map_assumption(ass).avoiding(child.obstructions) - for ass in algo.stretched_assumptions(cell) + forced_obs = algo.forced_obstructions_from_requirement( + self.gps, self.indices, cell, self.direction + ) + rem_req = algo.remaining_requirement_from_requirement( + self.gps, self.indices, cell + ) + mapped_parameters = [ + algo.multiplex_parameter(parameter, cell) + .add_obstructions_and_requirements(forced_obs, [rem_req]) + .apply_row_col_map(child.forward_map) + for parameter in comb_class.parameters ] - for assumption, mapped_assumption in zip( - comb_class.assumptions, mapped_assumptions + for parameter, mapped_parameter in zip( + comb_class.parameters, mapped_parameters ): - if mapped_assumption.gps: - parent_var = comb_class.get_assumption_parameter(assumption) - child_var = child.get_assumption_parameter(mapped_assumption) - extra_parameters[idx + 1 if self.include_empty else idx][ - parent_var - ] = child_var + parent_var = comb_class.get_parameter_name(parameter) + child_var = child.get_parameter_name(mapped_parameter) + extra_parameters[idx + 1 if self.include_empty else idx][ + parent_var + ] = child_var return extra_parameters def direction_string(self): diff --git a/tilings/strategies/verification.py b/tilings/strategies/verification.py index 9ef1d680..c6e19ef5 100644 --- a/tilings/strategies/verification.py +++ b/tilings/strategies/verification.py @@ -61,9 +61,7 @@ def get_terms(comb_class: CombinatorialClass, n: int) -> Terms: raise NotImplementedError gp = next(comb_class.minimal_gridded_perms()) if n == len(gp): - parameters = tuple( - assumption.get_value(gp) for assumption in comb_class.assumptions - ) + parameters = tuple(param.get_value(gp) for param in comb_class.parameters) return Counter([parameters]) return Counter() @@ -74,9 +72,7 @@ def get_objects(comb_class: CombinatorialClass, n: int) -> Objects: res: Objects = defaultdict(list) gp = next(comb_class.minimal_gridded_perms()) if n == len(gp): - parameters = tuple( - assumption.get_value(gp) for assumption in comb_class.assumptions - ) + parameters = tuple(param.get_value(gp) for param in comb_class.parameters) res[parameters].append(gp) return res @@ -112,10 +108,8 @@ def get_genf( cast(Tiling, comb_class) gp = next(comb_class.minimal_gridded_perms()) expected = {"x": len(gp)} - for assumption in comb_class.assumptions: - expected[ - comb_class.get_assumption_parameter(assumption) - ] = assumption.get_value(gp) + for param in comb_class.parameters: + expected[comb_class.get_parameter_name(param)] = param.get_value(gp) return reduce(mul, [var(k) ** n for k, n in expected.items()], 1) def __repr__(self) -> str: diff --git a/tilings/tiling.py b/tilings/tiling.py index 2ea6d60e..f98de472 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -241,8 +241,10 @@ def _remove_empty_rows_and_cols(self) -> None: self._cached_properties["forward_map"] = RowColMap.identity((0, 0)) self._obstructions = (GriddedPerm.single_cell((0,), (0, 0)),) self._requirements = tuple() - assert not self._parameters, "UH OH - we gotta think now" - self._parameters = tuple() + assert all( + all(not preimage.tiling.active_cells for preimage in parameter) + for parameter in self._parameters + ), "UH OH THINK EVEN HARDER- BLAME ÉMILE" self._cached_properties["dimensions"] = (1, 1) return forward_map = self._minimize_mapping() diff --git a/tox.ini b/tox.ini index 6d8f89a2..89a3e18c 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_sanity_check.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_point_placements.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_sanity_check.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From d4965336b79f0123ada37f6c15aa19c7f206ad72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Wed, 27 Oct 2021 15:37:08 +0000 Subject: [PATCH 13/41] Sanity check (#373) * add map repr * fix parameter counter repr * fix extra_parameter with include empty * handle extra_param when include empty is emtpy * sort the param when adding parameters * avoid duplicate parameter on tiling * fix sanity_check test to use new assumptions * reactivate sanity check test * reactivating a bunch of test that are actually passing --- tests/algorithms/test_enumeration.py | 8 - tests/strategies/test_sanity_check.py | 1017 ++++++++++--------- tests/test_bijections.py | 8 +- tests/test_tilescope.py | 6 +- tests/test_tiling.py | 1 - tilings/map.py | 3 + tilings/parameter_counter.py | 2 +- tilings/strategies/requirement_placement.py | 4 +- tilings/tiling.py | 5 +- tox.ini | 2 +- 10 files changed, 560 insertions(+), 496 deletions(-) diff --git a/tests/algorithms/test_enumeration.py b/tests/algorithms/test_enumeration.py index 637a1d2c..4a17469c 100644 --- a/tests/algorithms/test_enumeration.py +++ b/tests/algorithms/test_enumeration.py @@ -252,7 +252,6 @@ def test_not_verified(self, enum_with_list_req, onebyone_enum, enum_with_crossin ) assert not MonotoneTreeEnumeration(forest_tiling).verified() - @pytest.mark.xfail def test_get_genf(self, enum_verified): x = sympy.Symbol("x") expected_gf = -( @@ -272,7 +271,6 @@ def test_get_genf(self, enum_verified): expected_gf = -1 / ((x - 1) * (x / (x - 1) + 1)) assert sympy.simplify(enum_no_start.get_genf() - expected_gf) == 0 - @pytest.mark.xfail def test_get_genf_simple(self): t = Tiling( obstructions=[ @@ -285,7 +283,6 @@ def test_get_genf_simple(self): assert enum.verified() assert sympy.simplify(enum.get_genf() - sympy.sympify("1/(1-2*x)")) == 0 - @pytest.mark.xfail def test_with_finite_monotone_cell(self): t = Tiling( obstructions=[ @@ -300,7 +297,6 @@ def test_with_finite_monotone_cell(self): assert enum.verified() assert enum.get_genf().expand() == sympy.sympify("1+2*x+2*x**2") - @pytest.mark.xfail def test_with_finite_monotone_cell2(self): t = Tiling( obstructions=[ @@ -367,7 +363,6 @@ def test_interleave_fixed_lengths(self, enum_verified): + 20 * x ** 11 * dummy_var ** 3 * cell_var ** 3 ) - @pytest.mark.xfail def test_genf_with_req(self): t = Tiling( obstructions=[ @@ -386,7 +381,6 @@ def test_genf_with_req(self): terms = [0, 0, 0, 3, 10, 25, 56, 119, 246, 501, 1012] assert taylor_expand(genf) == terms - @pytest.mark.xfail def test_genf_with_big_finite_cell(self): t = Tiling( obstructions=[ @@ -412,7 +406,6 @@ def test_genf_with_big_finite_cell(self): + 20 * x ** 6 ) - @pytest.mark.xfail def test_with_two_reqs(self): t = Tiling( obstructions=( @@ -432,7 +425,6 @@ def test_with_two_reqs(self): assert enum.verified() assert taylor_expand(enum.get_genf()) == expected_enum - @pytest.mark.xfail def test_corner(self): t = Tiling( obstructions=( diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index 26834c92..3132ac3b 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -5,172 +5,146 @@ from comb_spec_searcher.strategies.rule import EquivalencePathRule from tilings import GriddedPerm, Tiling -from tilings.assumptions import TrackingAssumption -from tilings.strategies.assumption_insertion import AddAssumptionsStrategy +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategies.factor import FactorStrategy -from tilings.strategies.fusion import FusionStrategy -from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy from tilings.strategies.requirement_insertion import RequirementInsertionStrategy from tilings.strategies.requirement_placement import RequirementPlacementStrategy -from tilings.strategies.sliding import SlidingFactory rules_to_check = [ - RequirementInsertionStrategy( - gps=frozenset({GriddedPerm((0,), ((0, 0),))}), ignore_parent=True - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((0, 0), (1, 0))), - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((1, 0), ((0, 0), (1, 0))), - GriddedPerm((1, 0), ((1, 0), (1, 0))), - ), - requirements=((GriddedPerm((0,), ((1, 0),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), - ) - ), - RequirementInsertionStrategy( - gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((2, 0), (2, 0))), - GriddedPerm((0, 1), ((2, 0), (3, 0))), - GriddedPerm((0, 1), ((3, 0), (3, 0))), - GriddedPerm((1, 0), ((0, 0), (2, 0))), - GriddedPerm((1, 0), ((0, 0), (3, 0))), - GriddedPerm((1, 0), ((2, 0), (2, 0))), - GriddedPerm((1, 0), ((2, 0), (3, 0))), - GriddedPerm((1, 0), ((3, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), - GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), - GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), - ), - requirements=( - (GriddedPerm((0,), ((0, 0),)),), - (GriddedPerm((0,), ((3, 0),)),), - ), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), - ) - ), - FusionStrategy(col_idx=1, tracked=True)( - Tiling( - obstructions=( - GriddedPerm((0,), ((0, 0),)), - GriddedPerm((0,), ((1, 0),)), - GriddedPerm((0,), ((1, 1),)), - GriddedPerm((0,), ((2, 0),)), - GriddedPerm((0,), ((2, 1),)), - GriddedPerm((0,), ((3, 1),)), - GriddedPerm((0,), ((3, 2),)), - GriddedPerm((0, 1), ((1, 2), (1, 2))), - GriddedPerm((0, 1), ((1, 2), (2, 2))), - GriddedPerm((0, 1), ((2, 2), (2, 2))), - GriddedPerm((0, 1), ((3, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (1, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (2, 2))), - GriddedPerm((0, 2, 1), ((0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 2, 1), ((0, 1), (1, 2), (1, 2))), - GriddedPerm((0, 2, 1), ((0, 1), (1, 2), (2, 2))), - GriddedPerm((0, 2, 1), ((0, 1), (2, 2), (2, 2))), - GriddedPerm((1, 0, 2), ((0, 2), (0, 1), (1, 2))), - GriddedPerm((1, 0, 2), ((0, 2), (0, 1), (2, 2))), - GriddedPerm((2, 0, 1), ((0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (1, 2))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (2, 2))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (1, 2), (1, 2))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (1, 2), (2, 2))), - GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (2, 2), (2, 2))), - GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (0, 2))), - GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (1, 2))), - GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (2, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (0, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (1, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (2, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (1, 2), (1, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (1, 2), (2, 2))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (2, 2), (2, 2))), - GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (0, 2))), - GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (1, 2))), - GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (2, 2))), - ), - requirements=( - (GriddedPerm((0,), ((1, 2),)),), - (GriddedPerm((0,), ((2, 2),)),), - (GriddedPerm((0,), ((3, 0),)),), - ), - assumptions=( - TrackingAssumption( - ( - GriddedPerm((0,), ((2, 2),)), - GriddedPerm((0,), ((3, 0),)), - ) - ), - ), - ) - ), - RequirementInsertionStrategy( - gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 1), (0, 1))), - GriddedPerm((0, 1), ((0, 1), (0, 2))), - GriddedPerm((0, 1), ((0, 2), (0, 2))), - GriddedPerm((1, 0), ((0, 1), (0, 0))), - GriddedPerm((1, 0), ((0, 1), (0, 1))), - GriddedPerm((1, 0), ((0, 2), (0, 0))), - GriddedPerm((1, 0), ((0, 2), (0, 1))), - GriddedPerm((1, 0), ((0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), - GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), - ), - requirements=((GriddedPerm((0,), ((0, 1),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), - ) - ), - FactorStrategy([[(0, 0)], [(1, 1)]])( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((1, 0), ((1, 1), (1, 1))), - ), - ) - ), - FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((1, 0), ((1, 1), (1, 1))), - GriddedPerm((0, 1), ((2, 2), (2, 2))), - GriddedPerm((1, 0), ((2, 2), (2, 2))), - ), - requirements=( - (GriddedPerm((0,), ((0, 0),)),), - (GriddedPerm((0,), ((2, 2),)),), - ), - ) - ), + # RequirementInsertionStrategy( + # gps=frozenset({GriddedPerm((0,), ((0, 0),))}), ignore_parent=True + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1), ((0, 0), (1, 0))), + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((1, 0), ((0, 0), (1, 0))), + # GriddedPerm((1, 0), ((1, 0), (1, 0))), + # ), + # requirements=((GriddedPerm((0,), ((1, 0),)),),), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), + # ) + # ), + # RequirementInsertionStrategy( + # gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((0, 1), ((2, 0), (2, 0))), + # GriddedPerm((0, 1), ((2, 0), (3, 0))), + # GriddedPerm((0, 1), ((3, 0), (3, 0))), + # GriddedPerm((1, 0), ((0, 0), (2, 0))), + # GriddedPerm((1, 0), ((0, 0), (3, 0))), + # GriddedPerm((1, 0), ((2, 0), (2, 0))), + # GriddedPerm((1, 0), ((2, 0), (3, 0))), + # GriddedPerm((1, 0), ((3, 0), (3, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + # GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + # ), + # requirements=( + # (GriddedPerm((0,), ((0, 0),)),), + # (GriddedPerm((0,), ((3, 0),)),), + # ), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), + # ) + # ), + # FusionStrategy(col_idx=1, tracked=True)( + # Tiling( + # obstructions=( + # GriddedPerm((0,), ((0, 0),)), + # GriddedPerm((0,), ((1, 0),)), + # GriddedPerm((0,), ((1, 1),)), + # GriddedPerm((0,), ((2, 0),)), + # GriddedPerm((0,), ((2, 1),)), + # GriddedPerm((0,), ((3, 1),)), + # GriddedPerm((0,), ((3, 2),)), + # GriddedPerm((0, 1), ((1, 2), (1, 2))), + # GriddedPerm((0, 1), ((1, 2), (2, 2))), + # GriddedPerm((0, 1), ((2, 2), (2, 2))), + # GriddedPerm((0, 1), ((3, 0), (3, 0))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (1, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (2, 2))), + # GriddedPerm((0, 2, 1), ((0, 1), (0, 1), (0, 1))), + # GriddedPerm((0, 2, 1), ((0, 1), (1, 2), (1, 2))), + # GriddedPerm((0, 2, 1), ((0, 1), (1, 2), (2, 2))), + # GriddedPerm((0, 2, 1), ((0, 1), (2, 2), (2, 2))), + # GriddedPerm((1, 0, 2), ((0, 2), (0, 1), (1, 2))), + # GriddedPerm((1, 0, 2), ((0, 2), (0, 1), (2, 2))), + # GriddedPerm((2, 0, 1), ((0, 1), (0, 1), (0, 1))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (1, 2))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (0, 2), (2, 2))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (1, 2), (1, 2))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (1, 2), (2, 2))), + # GriddedPerm((0, 1, 3, 2), ((0, 2), (0, 2), (2, 2), (2, 2))), + # GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (0, 2))), + # GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (1, 2))), + # GriddedPerm((0, 2, 1, 3), ((0, 2), (0, 2), (0, 2), (2, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (0, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (1, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (2, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (1, 2), (1, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (1, 2), (2, 2))), + # GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (2, 2), (2, 2))), + # GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (0, 2))), + # GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (1, 2))), + # GriddedPerm((2, 0, 1, 3), ((0, 2), (0, 2), (0, 2), (2, 2))), + # ), + # requirements=( + # (GriddedPerm((0,), ((1, 2),)),), + # (GriddedPerm((0,), ((2, 2),)),), + # (GriddedPerm((0,), ((3, 0),)),), + # ), + # assumptions=( + # TrackingAssumption( + # ( + # GriddedPerm((0,), ((2, 2),)), + # GriddedPerm((0,), ((3, 0),)), + # ) + # ), + # ), + # ) + # ), + # RequirementInsertionStrategy( + # gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 1), (0, 1))), + # GriddedPerm((0, 1), ((0, 1), (0, 2))), + # GriddedPerm((0, 1), ((0, 2), (0, 2))), + # GriddedPerm((1, 0), ((0, 1), (0, 0))), + # GriddedPerm((1, 0), ((0, 1), (0, 1))), + # GriddedPerm((1, 0), ((0, 2), (0, 0))), + # GriddedPerm((1, 0), ((0, 2), (0, 1))), + # GriddedPerm((1, 0), ((0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + # ), + # requirements=((GriddedPerm((0,), ((0, 1),)),),), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), + # ) + # ), FactorStrategy([[(0, 0)], [(1, 1)]])( Tiling( obstructions=( GriddedPerm((0, 1), ((0, 0), (0, 0))), GriddedPerm((1, 0), ((1, 1), (1, 1))), ), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), ) ), FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( @@ -185,263 +159,286 @@ (GriddedPerm((0,), ((0, 0),)),), (GriddedPerm((0,), ((2, 2),)),), ), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((2, 2),))), - ), - ), - ), - ), - FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((1, 0), ((1, 1), (1, 1))), - GriddedPerm((0, 1), ((2, 2), (2, 2))), - GriddedPerm((1, 0), ((2, 2), (2, 2))), - ), - requirements=( - (GriddedPerm((0,), ((0, 0),)),), - (GriddedPerm((0,), ((2, 2),)),), - ), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - TrackingAssumption((GriddedPerm((0,), ((2, 2),)),)), - ), - ), - ), - list( - SlidingFactory(use_symmetries=True)( - Tiling( - obstructions=( - GriddedPerm((1, 0), ((2, 0), (2, 0))), - GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), - GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (2, 0))), - GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (3, 0))), - GriddedPerm((2, 1, 0), ((1, 0), (2, 0), (3, 0))), - GriddedPerm((2, 1, 0), ((1, 0), (3, 0), (3, 0))), - GriddedPerm((2, 1, 0), ((2, 0), (3, 0), (3, 0))), - GriddedPerm((2, 1, 0), ((3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (1, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (2, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (3, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (1, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (2, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (3, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (2, 0), (3, 0))), - GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (3, 0), (3, 0))), - ), - requirements=(), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((2, 0),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((2, 0),)), GriddedPerm((0,), ((3, 0),))) - ), - TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)), - ), - ) - ) - )[1], - RequirementInsertionStrategy( - gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((2, 0), (2, 0))), - GriddedPerm((0, 1), ((2, 0), (3, 0))), - GriddedPerm((0, 1), ((3, 0), (3, 0))), - GriddedPerm((1, 0), ((0, 0), (2, 0))), - GriddedPerm((1, 0), ((0, 0), (3, 0))), - GriddedPerm((1, 0), ((2, 0), (2, 0))), - GriddedPerm((1, 0), ((2, 0), (3, 0))), - GriddedPerm((1, 0), ((3, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), - GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), - GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), - ), - requirements=( - (GriddedPerm((0,), ((0, 0),)),), - (GriddedPerm((0,), ((3, 0),)),), - ), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), - ) - ), - RequirementInsertionStrategy( - gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 1), (0, 1))), - GriddedPerm((0, 1), ((0, 1), (0, 2))), - GriddedPerm((0, 1), ((0, 2), (0, 2))), - GriddedPerm((1, 0), ((0, 1), (0, 0))), - GriddedPerm((1, 0), ((0, 1), (0, 1))), - GriddedPerm((1, 0), ((0, 2), (0, 0))), - GriddedPerm((1, 0), ((0, 2), (0, 1))), - GriddedPerm((1, 0), ((0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), - GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), - ), - requirements=((GriddedPerm((0,), ((0, 1),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), - ) - ), - FusionStrategy(row_idx=2, tracked=True)( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 2), (0, 2))), - GriddedPerm((0, 1), ((0, 2), (0, 3))), - GriddedPerm((0, 1), ((0, 3), (0, 3))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 3))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 1))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 2))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 3))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 1))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 2))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 3))), - ), - requirements=(), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 1),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((0, 2),))) - ), - TrackingAssumption((GriddedPerm((0,), ((0, 3),)),)), - ), - ) - ), - FusionStrategy(row_idx=3, tracked=True)( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 2), (0, 2))), - GriddedPerm((0, 1), ((1, 1), (1, 1))), - GriddedPerm((0, 1), ((1, 3), (1, 3))), - GriddedPerm((0, 1), ((1, 3), (1, 4))), - GriddedPerm((0, 1), ((1, 4), (1, 4))), - GriddedPerm((1, 0), ((0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 1))), - ), - requirements=((GriddedPerm((0,), ((0, 2),)),),), - assumptions=( - TrackingAssumption( - (GriddedPerm((0,), ((0, 2),)), GriddedPerm((0,), ((1, 1),))) - ), - TrackingAssumption( - (GriddedPerm((0,), ((1, 3),)), GriddedPerm((0,), ((1, 4),))) - ), - TrackingAssumption((GriddedPerm((0,), ((1, 4),)),)), - ), - ) - ), - FusionStrategy(row_idx=1, tracked=True)( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((0, 1), (0, 1))), - GriddedPerm((0, 1), ((0, 1), (0, 2))), - GriddedPerm((0, 1), ((0, 2), (0, 2))), - GriddedPerm((0, 1), ((0, 4), (0, 4))), - GriddedPerm((0, 1), ((1, 3), (1, 3))), - GriddedPerm((1, 0), ((1, 3), (1, 3))), - ), - requirements=((GriddedPerm((0,), ((1, 3),)),),), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 1),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((0, 2),))) - ), - TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 4),)), GriddedPerm((0,), ((1, 3),))) - ), - ), - ) - ), - FusionStrategy(row_idx=1, col_idx=None, tracked=True)( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 2), (0, 2), (0, 2))), - ), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((0, 1),))) - ), - TrackingAssumption( - ( - GriddedPerm((0,), ((0, 0),)), - GriddedPerm((0,), ((0, 1),)), - GriddedPerm((0,), ((0, 2),)), - ) - ), - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((0, 2),))) - ), - TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)), - ), - ) - ), - RearrangeAssumptionStrategy( - assumption=TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) - ), - sub_assumption=TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)), - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((2, 0), (2, 0))), - ), - requirements=(), - assumptions=( - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) - ), - TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)), - ), - ) - ), - AddAssumptionsStrategy( - assumptions=( - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) - ), - ), - workable=False, - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((2, 0), (2, 0))), - ), - requirements=(), - assumptions=(), ) ), + # FactorStrategy([[(0, 0)], [(1, 1)]])( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((1, 0), ((1, 1), (1, 1))), + # ), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), + # ) + # ), + # FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((1, 0), ((1, 1), (1, 1))), + # GriddedPerm((0, 1), ((2, 2), (2, 2))), + # GriddedPerm((1, 0), ((2, 2), (2, 2))), + # ), + # requirements=( + # (GriddedPerm((0,), ((0, 0),)),), + # (GriddedPerm((0,), ((2, 2),)),), + # ), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((2, 2),))), + # ), + # ), + # ), + # ), + # FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((1, 0), ((1, 1), (1, 1))), + # GriddedPerm((0, 1), ((2, 2), (2, 2))), + # GriddedPerm((1, 0), ((2, 2), (2, 2))), + # ), + # requirements=( + # (GriddedPerm((0,), ((0, 0),)),), + # (GriddedPerm((0,), ((2, 2),)),), + # ), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), + # TrackingAssumption((GriddedPerm((0,), ((2, 2),)),)), + # ), + # ), + # ), + # list( + # SlidingFactory(use_symmetries=True)( + # Tiling( + # obstructions=( + # GriddedPerm((1, 0), ((2, 0), (2, 0))), + # GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), + # GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (2, 0))), + # GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (3, 0))), + # GriddedPerm((2, 1, 0), ((1, 0), (2, 0), (3, 0))), + # GriddedPerm((2, 1, 0), ((1, 0), (3, 0), (3, 0))), + # GriddedPerm((2, 1, 0), ((2, 0), (3, 0), (3, 0))), + # GriddedPerm((2, 1, 0), ((3, 0), (3, 0), (3, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (1, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (2, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (0, 0), (3, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (1, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (2, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (1, 0), (3, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (2, 0), (3, 0))), + # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 0), (3, 0), (3, 0))), + # ), + # requirements=(), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((2, 0),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((2, 0),)), GriddedPerm((0,), ((3, 0),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)), + # ), + # ) + # ) + # )[1], + # RequirementInsertionStrategy( + # gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((0, 1), ((2, 0), (2, 0))), + # GriddedPerm((0, 1), ((2, 0), (3, 0))), + # GriddedPerm((0, 1), ((3, 0), (3, 0))), + # GriddedPerm((1, 0), ((0, 0), (2, 0))), + # GriddedPerm((1, 0), ((0, 0), (3, 0))), + # GriddedPerm((1, 0), ((2, 0), (2, 0))), + # GriddedPerm((1, 0), ((2, 0), (3, 0))), + # GriddedPerm((1, 0), ((3, 0), (3, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + # GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + # ), + # requirements=( + # (GriddedPerm((0,), ((0, 0),)),), + # (GriddedPerm((0,), ((3, 0),)),), + # ), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), + # ) + # ), + # RequirementInsertionStrategy( + # gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 1), (0, 1))), + # GriddedPerm((0, 1), ((0, 1), (0, 2))), + # GriddedPerm((0, 1), ((0, 2), (0, 2))), + # GriddedPerm((1, 0), ((0, 1), (0, 0))), + # GriddedPerm((1, 0), ((0, 1), (0, 1))), + # GriddedPerm((1, 0), ((0, 2), (0, 0))), + # GriddedPerm((1, 0), ((0, 2), (0, 1))), + # GriddedPerm((1, 0), ((0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + # GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + # ), + # requirements=((GriddedPerm((0,), ((0, 1),)),),), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), + # ) + # ), + # FusionStrategy(row_idx=2, tracked=True)( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 2), (0, 2))), + # GriddedPerm((0, 1), ((0, 2), (0, 3))), + # GriddedPerm((0, 1), ((0, 3), (0, 3))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 3))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 0))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 1))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 2))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 0), (0, 3))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 1))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 2))), + # GriddedPerm((0, 1, 2, 3), ((0, 0), (0, 0), (0, 1), (0, 3))), + # ), + # requirements=(), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((0, 1),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((0, 2),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((0, 3),)),)), + # ), + # ) + # ), + # FusionStrategy(row_idx=3, tracked=True)( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 2), (0, 2))), + # GriddedPerm((0, 1), ((1, 1), (1, 1))), + # GriddedPerm((0, 1), ((1, 3), (1, 3))), + # GriddedPerm((0, 1), ((1, 3), (1, 4))), + # GriddedPerm((0, 1), ((1, 4), (1, 4))), + # GriddedPerm((1, 0), ((0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + # GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 1))), + # ), + # requirements=((GriddedPerm((0,), ((0, 2),)),),), + # assumptions=( + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 2),)), GriddedPerm((0,), ((1, 1),))) + # ), + # TrackingAssumption( + # (GriddedPerm((0,), ((1, 3),)), GriddedPerm((0,), ((1, 4),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((1, 4),)),)), + # ), + # ) + # ), + # FusionStrategy(row_idx=1, tracked=True)( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1), ((0, 1), (0, 1))), + # GriddedPerm((0, 1), ((0, 1), (0, 2))), + # GriddedPerm((0, 1), ((0, 2), (0, 2))), + # GriddedPerm((0, 1), ((0, 4), (0, 4))), + # GriddedPerm((0, 1), ((1, 3), (1, 3))), + # GriddedPerm((1, 0), ((1, 3), (1, 3))), + # ), + # requirements=((GriddedPerm((0,), ((1, 3),)),),), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((0, 1),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((0, 2),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 4),)), GriddedPerm((0,), ((1, 3),))) + # ), + # ), + # ) + # ), + # FusionStrategy(row_idx=1, col_idx=None, tracked=True)( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 0), (0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), + # GriddedPerm((0, 1, 2), ((0, 2), (0, 2), (0, 2))), + # ), + # assumptions=( + # TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((0, 1),))) + # ), + # TrackingAssumption( + # ( + # GriddedPerm((0,), ((0, 0),)), + # GriddedPerm((0,), ((0, 1),)), + # GriddedPerm((0,), ((0, 2),)), + # ) + # ), + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((0, 2),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)), + # ), + # ) + # ), + # RearrangeAssumptionStrategy( + # assumption=TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) + # ), + # sub_assumption=TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)), + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((0, 1), ((2, 0), (2, 0))), + # ), + # requirements=(), + # assumptions=( + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) + # ), + # TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)), + # ), + # ) + # ), + # AddAssumptionsStrategy( + # assumptions=( + # TrackingAssumption( + # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) + # ), + # ), + # workable=False, + # )( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((0, 1), ((2, 0), (2, 0))), + # ), + # requirements=(), + # assumptions=(), + # ) + # ), RequirementPlacementStrategy( gps=(GriddedPerm((0,), ((0, 0),)),), indices=(0,), @@ -453,8 +450,25 @@ )( Tiling( obstructions=(GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))),), - requirements=(), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((1, 2, 0), ((1, 0), (1, 0), (1, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0}), + ), + ) + ), + ), ) ), RequirementInsertionStrategy( @@ -485,7 +499,6 @@ GriddedPerm((2, 0, 1, 3), ((1, 0), (1, 0), (1, 0), (1, 0))), ), requirements=((GriddedPerm((0,), ((0, 0),)),),), - assumptions=(), ) ), RequirementPlacementStrategy( @@ -506,7 +519,31 @@ GriddedPerm((1, 2, 0), ((0, 0), (0, 2), (0, 0))), ), requirements=((GriddedPerm((0,), ((1, 1),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 3), (0, 3))), + GriddedPerm((0, 1), ((1, 2), (1, 2))), + GriddedPerm((1, 0), ((1, 2), (1, 2))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 1), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 3), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 1), (0, 1), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 1), (0, 1), (0, 1))), + GriddedPerm((1, 2, 0), ((0, 1), (0, 3), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 1), (0, 3), (0, 1))), + ), + requirements=((GriddedPerm((0,), ((1, 2),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1, 3: 2}, {0: 0, 1: 1}), + ), + ) + ), + ), ) ), RequirementPlacementStrategy( @@ -521,21 +558,44 @@ Tiling( obstructions=(GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))),), requirements=((GriddedPerm((0,), ((0, 0),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), - ) - ), - RequirementInsertionStrategy(gps=(GriddedPerm((0,), ((1, 0),)),),)( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((0, 0), (1, 0))), - GriddedPerm((1, 0), ((0, 0), (1, 0))), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((1, 2, 0), ((1, 0), (1, 0), (1, 0))), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((1, 0),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0}), + ), + ) + ), ), - requirements=((GriddedPerm((0,), ((0, 0),)),),), - assumptions=(TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)),), ) ), + # RequirementInsertionStrategy(gps=(GriddedPerm((0,), ((1, 0),)),),)( + # Tiling( + # obstructions=( + # GriddedPerm((0, 1), ((0, 0), (0, 0))), + # GriddedPerm((0, 1), ((1, 0), (1, 0))), + # GriddedPerm((0, 1), ((0, 0), (1, 0))), + # GriddedPerm((1, 0), ((0, 0), (1, 0))), + # ), + # requirements=((GriddedPerm((0,), ((0, 0),)),),), + # assumptions=(TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)),), + # ) + # ), ] equiv_rule_to_check = [r for r in rules_to_check if r.is_equivalence()] @@ -612,7 +672,59 @@ def test_pickle_rule(rule): def test_sanity_check_big_row_placement(): - rule = RequirementPlacementStrategy( + t = Tiling( + obstructions=[ + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (3, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3), ((0, 0), (1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2, 3), ((0, 0), (2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2, 3), ((1, 0), (1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2, 3), ((1, 0), (2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2, 3), ((1, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3), ((2, 0), (2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2, 3), ((2, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 1, 3, 2), ((1, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 1, 3, 2), ((2, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 2, 3, 1), ((1, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 2, 3, 1), ((2, 0), (2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3, 4), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3, 4), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3, 4), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3, 4), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 3, 4), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 4, 3), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 4, 3), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 4, 3), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 4, 3), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 2, 4, 3), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 3, 4, 2), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 3, 4, 2), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 3, 4, 2), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 3, 4, 2), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 1, 3, 4, 2), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 4, 1), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 4, 1), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 4, 1), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 4, 1), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 4, 1), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), + ] + ) + map_c0 = RowColMap({0: 0}, {0: 0, 1: 0, 2: 1, 3: 2, 4: 3}) + map_c1 = RowColMap({0: 0}, {0: 0, 1: 1, 2: 1, 3: 2, 4: 3}) + preimage_counter_c0 = PreimageCounter(map_c0.preimage_tiling(t), map_c0) + preimage_counter_c1 = PreimageCounter(map_c1.preimage_tiling(t), map_c1) + t = t.add_parameters( + [ + ParameterCounter([preimage_counter_c0]), + ParameterCounter([preimage_counter_c1]), + ParameterCounter([preimage_counter_c0, preimage_counter_c1]), + ] + ) + strat = RequirementPlacementStrategy( gps=( GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((3, 0),)), @@ -625,55 +737,6 @@ def test_sanity_check_big_row_placement(): own_row=True, ignore_parent=False, include_empty=True, - )( - Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 0))), - GriddedPerm((0, 1), ((0, 0), (3, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (3, 0))), - GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (1, 0), (2, 0), (2, 0))), - GriddedPerm((0, 1, 2, 3), ((0, 0), (2, 0), (2, 0), (2, 0))), - GriddedPerm((0, 1, 2, 3), ((1, 0), (1, 0), (2, 0), (2, 0))), - GriddedPerm((0, 1, 2, 3), ((1, 0), (2, 0), (2, 0), (2, 0))), - GriddedPerm((0, 1, 2, 3), ((1, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3), ((2, 0), (2, 0), (2, 0), (2, 0))), - GriddedPerm((0, 1, 2, 3), ((2, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 1, 3, 2), ((1, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 1, 3, 2), ((2, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 2, 3, 1), ((1, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 2, 3, 1), ((2, 0), (2, 0), (2, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3, 4), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3, 4), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3, 4), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3, 4), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 3, 4), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 4, 3), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 4, 3), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 4, 3), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 4, 3), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 2, 4, 3), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 3, 4, 2), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 3, 4, 2), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 3, 4, 2), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 3, 4, 2), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 1, 3, 4, 2), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 2, 3, 4, 1), ((1, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 2, 3, 4, 1), ((1, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 2, 3, 4, 1), ((2, 0), (2, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 2, 3, 4, 1), ((2, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - GriddedPerm((0, 2, 3, 4, 1), ((3, 0), (3, 0), (3, 0), (3, 0), (3, 0))), - ), - requirements=(), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))) - ), - TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)), - ), - ) ) + rule = strat(t) rule.sanity_check(2) diff --git a/tests/test_bijections.py b/tests/test_bijections.py index c724a080..7cdefff0 100644 --- a/tests/test_bijections.py +++ b/tests/test_bijections.py @@ -28,8 +28,6 @@ from tilings.strategies.sliding import SlidingFactory, SlidingStrategy from tilings.tilescope import TileScope, TileScopePack -pytestmark = pytest.mark.xfail - def find_bijection_between( searcher1: CombinatorialSpecificationSearcher, @@ -151,6 +149,7 @@ def test_bijection_7(): ) +@pytest.mark.xfail def test_bijection_8_cross_domain(): # flake8: noqa _import_css_example() @@ -182,6 +181,7 @@ def test_bijection_8_cross_domain(): _bijection_asserter(find_bijection_between(searcher2, searcher1)) +@pytest.mark.xfail def test_bijection_9_cross_domain(): # flake8: noqa _import_css_example() @@ -487,6 +487,7 @@ def test_bijection_14_json(): _bijection_asserter(Bijection.from_dict(json.loads(json.dumps(bi.to_jsonable())))) +@pytest.mark.xfail @pytest.mark.slow def test_bijection_15_fusion(): pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) @@ -799,6 +800,7 @@ def test_bijection_15_fusion(): ) +@pytest.mark.xfail def test_bijection_16_fusion_json(): pack = TileScopePack( initial_strats=[strat.FactorFactory()], @@ -828,6 +830,7 @@ def test_bijection_16_fusion_json(): _bijection_asserter(bi_from_dict) +@pytest.mark.xfail def test_atom_assumption_path_mismatch(): path1 = [ ( @@ -1278,6 +1281,7 @@ def test_atom_assumption_path_mismatch(): ).assumptions_match_down_to_atom() +@pytest.mark.xfail def test_atom_assumption_path_match(): path1 = [ ( diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index 58de4e0b..d9d35cd0 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -18,8 +18,6 @@ class ComponentFusionStrategy: pass -pytestmark = pytest.mark.xfail - point_placements = TileScopePack.point_placements() all_the_strategies_verify_database = TileScopePack.all_the_strategies().make_database() all_the_strategies_fusion = TileScopePack.all_the_strategies().make_fusion( @@ -106,6 +104,7 @@ def test_132_321_genf(): ] +@pytest.mark.xfail @pytest.mark.timeout(20) def test_123(): searcher = TileScope((Perm((0, 1, 2)),), point_placements_fusion) @@ -120,6 +119,7 @@ def test_123_with_db(): assert isinstance(spec, CombinatorialSpecification) +@pytest.mark.xfail @pytest.mark.timeout(20) def test_1342_1423(): point_placements_component_fusion = point_placements.make_fusion( @@ -305,6 +305,7 @@ def test_expansion(): spec = spec.expand_verified() +@pytest.mark.xfail @pytest.mark.timeout(30) def test_domino(): domino = Tiling( @@ -383,6 +384,7 @@ def forest_expansion(): ] +@pytest.mark.xfail def test_guided_searcher(): tilescope = TileScope( "123", TileScopePack.point_placements().make_fusion(tracked=False) diff --git a/tests/test_tiling.py b/tests/test_tiling.py index 37ba00ca..96cdfa40 100644 --- a/tests/test_tiling.py +++ b/tests/test_tiling.py @@ -2351,7 +2351,6 @@ def test_is_atom(): assert not empty_set.is_atom() -@pytest.mark.xfail class TestGetGenf: """ Group all the test regarding getting the generating function for a tiling. diff --git a/tilings/map.py b/tilings/map.py index e7cad59f..900f99de 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -404,6 +404,9 @@ def __str__(self) -> str: s += f" col map: {{{col_str}}}" return s + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._row_map!r}, {self._col_map!r})" + def __eq__(self, other: object) -> bool: if not isinstance(other, RowColMap): return NotImplemented diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index b327fd27..35c02439 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -104,7 +104,7 @@ def __hash__(self) -> int: return hash((self.tiling, self.map)) def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.tiling!r}), {self.map!r})" + return f"{self.__class__.__name__}({self.tiling!r}, {self.map!r})" def __str__(self): map_str = " " + str(self.map).replace("\n", "\n ") diff --git a/tilings/strategies/requirement_placement.py b/tilings/strategies/requirement_placement.py index 815b70b0..98ed74c6 100644 --- a/tilings/strategies/requirement_placement.py +++ b/tilings/strategies/requirement_placement.py @@ -88,11 +88,11 @@ def extra_parameters( raise StrategyDoesNotApply("Strategy does not apply") algo = self.placement_class(comb_class) extra_parameters: Tuple[Dict[str, str], ...] = tuple({} for _ in children) - if self.include_empty: + if self.include_empty and not children[0].is_empty(): child = children[0] for parameter in comb_class.parameters: mapped_parameter = parameter.add_obstructions_and_requirements( - child.obstructions, [] + self.gps, [] ).apply_row_col_map(child.forward_map) parent_var = comb_class.get_parameter_name(parameter) child_var = child.get_parameter_name(mapped_parameter) diff --git a/tilings/tiling.py b/tilings/tiling.py index f98de472..4de21d7d 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -122,7 +122,7 @@ def __init__( # Set of requirement lists self._requirements = Tiling.sort_requirements(requirements) # Set of parameters - self._parameters = tuple(sorted(parameters)) + self._parameters = tuple(sorted(set(parameters))) # Simplify the set of obstructions and the set of requirement lists if simplify: @@ -615,10 +615,11 @@ def add_parameter(self, parameter: ParameterCounter) -> "Tiling": def add_parameters(self, parameters: Iterable[ParameterCounter]) -> "Tiling": """Returns a new tiling with the added parameters.""" + sorted_params = sorted(chain(self._parameters, parameters)) tiling = Tiling( self._obstructions, self._requirements, - self._parameters + tuple(parameters), + sorted_params, remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, diff --git a/tox.ini b/tox.ini index 89a3e18c..def37d45 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_sanity_check.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From 5674b819da205f1075a02e77f10edb4e06c6b3d7 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 29 Oct 2021 14:02:03 +0000 Subject: [PATCH 14/41] using initialiser only once for preimages (#374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * using initialiser only once for preimages * refactored with smaller method * remove todo Co-authored-by: Émile Nadeau --- tilings/algorithms/requirement_placement.py | 61 +++++++++++++++------ tilings/map.py | 20 ++++++- tilings/strategies/requirement_placement.py | 10 +++- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/tilings/algorithms/requirement_placement.py b/tilings/algorithms/requirement_placement.py index 2c29cc42..0b4f2b5d 100644 --- a/tilings/algorithms/requirement_placement.py +++ b/tilings/algorithms/requirement_placement.py @@ -15,6 +15,12 @@ ObsCache = Dict[Cell, List[GriddedPerm]] ReqsCache = Dict[Cell, List[ListRequirement]] ParamCache = Dict[Cell, List[ParameterCounter]] +UninitialisedPreimage = Tuple[ + List[GriddedPerm], + List[List[GriddedPerm]], + RowColMap, +] +UninitialisedParameter = List[UninitialisedPreimage] class MultiplexMap(RowColMap): @@ -291,15 +297,15 @@ def multiplex_map(self, width: int, height: int, cell: Cell) -> RowColMap: def multiplex_tiling( self, tiling: "Tiling", cell: Cell - ) -> Tuple[List[GriddedPerm], List[List[GriddedPerm]], List[ParameterCounter]]: + ) -> Tuple[ + List[GriddedPerm], List[List[GriddedPerm]], List[UninitialisedParameter] + ]: """ Return the tiling created by 'multipexing' in cell. That is stretching the cell to be a 3x3 square. """ # TODO: cache this? row_col_map = self.multiplex_map(*tiling.dimensions, cell) - # TODO: should we optimise this to stop adding gps that are in cells we will - # later mark as empty? obs, reqs = row_col_map.preimage_obstruction_and_requirements( tiling.remove_parameters() ) @@ -314,12 +320,11 @@ def multiplex_tiling( def multiplex_preimage( self, preimage: PreimageCounter, cell: Cell - ) -> List[PreimageCounter]: + ) -> Iterator[UninitialisedPreimage]: """ - Return the list of preimages whose sum count the number of preimages + Return the iterator of preimages whose sum count the number of preimages when cell is multiplexed into a 3x3. """ - res: List[PreimageCounter] = [] width, height = preimage.tiling.dimensions if self.own_col: width += 2 @@ -329,9 +334,7 @@ def multiplex_preimage( preimage_multiplex_map = self.multiplex_map( *preimage.tiling.dimensions, precell ) - multiplex_tiling = preimage.tiling.__class__( - *self.multiplex_tiling(preimage.tiling, precell) - ) + obs, reqs, _ = self.multiplex_tiling(preimage.tiling, precell) col_map = {} for x in range(width): shift = ( @@ -356,21 +359,45 @@ def multiplex_preimage( row_map[y] = ( preimage.map.map_row(preimage_multiplex_map.map_row(y)) + shift ) - res.append(PreimageCounter(multiplex_tiling, RowColMap(row_map, col_map))) - return res + yield obs, reqs, RowColMap(row_map, col_map) def multiplex_parameter( self, parameter: ParameterCounter, cell: Cell - ) -> ParameterCounter: + ) -> UninitialisedParameter: """ Return the parameter when cell has been multiplexed into a 3x3. """ - return ParameterCounter( + return list( itertools.chain.from_iterable( self.multiplex_preimage(preimage, cell) for preimage in parameter ) ) + def add_forced_obs_and_reqs_to_param( + self, + param: UninitialisedParameter, + forced_obs: List[GriddedPerm], + rem_req: List[GriddedPerm], + ) -> ParameterCounter: + """ + Takes int a uninitialised parameter adds the forced obstruction and the + remaining_requirement and return the initialised parameter. + """ + preimage_counters = [] + for pobs, preqs, row_col_map in param: + pforced_obs = list(row_col_map.preimage_gps(forced_obs)) + preduced_obs = ( + o1 for o1 in pobs if not any(o2 in o1 for o2 in pforced_obs) + ) + prem_req = list(row_col_map.preimage_gps(rem_req)) + preimage_tiling = self._tiling.__class__( + itertools.chain(preduced_obs, pforced_obs), + itertools.chain(preqs, [prem_req]), + already_minimized_obs=True, + ) + preimage_counters.append(PreimageCounter(preimage_tiling, row_col_map)) + return ParameterCounter(preimage_counters) + def place_point_of_req( self, gps: Iterable[GriddedPerm], indices: Iterable[int], direction: Dir ) -> Tuple["Tiling", ...]: @@ -388,15 +415,15 @@ def place_point_of_req( ) reduced_obs = [o1 for o1 in obs if not any(o2 in o1 for o2 in forced_obs)] rem_req = self.remaining_requirement_from_requirement(gps, indices, cell) - params = [ - param.add_obstructions_and_requirements(forced_obs, [rem_req]) + new_params = ( + self.add_forced_obs_and_reqs_to_param(param, forced_obs, rem_req) for param in params - ] + ) res.append( self._tiling.__class__( reduced_obs + forced_obs, reqs + [rem_req], - params, + new_params, already_minimized_obs=True, ) ) diff --git a/tilings/map.py b/tilings/map.py index 900f99de..eb097584 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -1,5 +1,14 @@ import itertools -from typing import TYPE_CHECKING, Callable, Dict, Iterator, List, Optional, Tuple +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Tuple, +) from tilings.exception import InvalidOperationError from tilings.griddedperm import GriddedPerm @@ -368,6 +377,15 @@ def preimage_gp(self, gp: "GriddedPerm") -> Iterator["GriddedPerm"]: new_gp = gp.__class__(gp.patt, zip(*pos)) yield new_gp + def preimage_gps(self, gps: Iterable["GriddedPerm"]) -> Iterator["GriddedPerm"]: + """ + Returns all the preimages of the given gridded permutations. + + Gridded permutations that are contradictory are filtered out. + """ + for gp in gps: + yield from self.preimage_gp(gp) + def preimage_obstruction_and_requirements( self, tiling: "Tiling" ) -> Tuple[List[GriddedPerm], List[List[GriddedPerm]]]: diff --git a/tilings/strategies/requirement_placement.py b/tilings/strategies/requirement_placement.py index 98ed74c6..4fda6aa1 100644 --- a/tilings/strategies/requirement_placement.py +++ b/tilings/strategies/requirement_placement.py @@ -10,6 +10,7 @@ from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS from tilings import GriddedPerm, Tiling from tilings.algorithms import RequirementPlacement +from tilings.parameter_counter import ParameterCounter, PreimageCounter __all__ = [ "PatternPlacementFactory", @@ -107,7 +108,14 @@ def extra_parameters( self.gps, self.indices, cell ) mapped_parameters = [ - algo.multiplex_parameter(parameter, cell) + ParameterCounter( + [ + PreimageCounter(Tiling(obs, reqs), row_col_map) + for obs, reqs, row_col_map in algo.multiplex_parameter( + parameter, cell + ) + ] + ) .add_obstructions_and_requirements(forced_obs, [rem_req]) .apply_row_col_map(child.forward_map) for parameter in comb_class.parameters From e12719e78130406c1b3de781e758c576180b1bcf Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 29 Oct 2021 14:22:13 +0000 Subject: [PATCH 15/41] Cell insertion (#376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * assumption -> parameter in req insertion * assumption -> parameter in inferral * remove magic VScode import * activate requirement insertion sanity check * activate inferral strategies test * should be applying row col map * add back inferral fixture Co-authored-by: Émile Nadeau --- tests/strategies/test_inferral.py | 2 + tests/strategies/test_sanity_check.py | 681 +++++++++++++++----- tilings/strategies/obstruction_inferral.py | 15 +- tilings/strategies/requirement_insertion.py | 26 +- tox.ini | 2 +- 5 files changed, 531 insertions(+), 195 deletions(-) diff --git a/tests/strategies/test_inferral.py b/tests/strategies/test_inferral.py index bba0d6ba..6504fa65 100644 --- a/tests/strategies/test_inferral.py +++ b/tests/strategies/test_inferral.py @@ -9,6 +9,8 @@ RowColumnSeparationStrategy, ) +pytest_plugins = ["tests.fixtures.simple_trans"] + @pytest.fixture def tiling1(): diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index b6d18b9e..eef73e2d 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -12,51 +12,127 @@ from tilings.strategies.requirement_placement import RequirementPlacementStrategy rules_to_check = [ - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((0, 0),))}), ignore_parent=True - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 0), (0, 0))), - # GriddedPerm((0, 1), ((0, 0), (1, 0))), - # GriddedPerm((0, 1), ((1, 0), (1, 0))), - # GriddedPerm((1, 0), ((0, 0), (1, 0))), - # GriddedPerm((1, 0), ((1, 0), (1, 0))), - # ), - # requirements=((GriddedPerm((0,), ((1, 0),)),),), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), - # ) - # ), - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((1, 0), (1, 0))), - # GriddedPerm((0, 1), ((2, 0), (2, 0))), - # GriddedPerm((0, 1), ((2, 0), (3, 0))), - # GriddedPerm((0, 1), ((3, 0), (3, 0))), - # GriddedPerm((1, 0), ((0, 0), (2, 0))), - # GriddedPerm((1, 0), ((0, 0), (3, 0))), - # GriddedPerm((1, 0), ((2, 0), (2, 0))), - # GriddedPerm((1, 0), ((2, 0), (3, 0))), - # GriddedPerm((1, 0), ((3, 0), (3, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), - # GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), - # ), - # requirements=( - # (GriddedPerm((0,), ((0, 0),)),), - # (GriddedPerm((0,), ((3, 0),)),), - # ), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), - # ) - # ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((0, 0),))}), ignore_parent=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((1, 0), ((0, 0), (1, 0))), + GriddedPerm((1, 0), ((1, 0), (1, 0))), + ), + requirements=((GriddedPerm((0,), ((1, 0),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((0, 0), (2, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (1, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + GriddedPerm((1, 0), ((1, 0), (1, 0))), + GriddedPerm((1, 0), ((1, 0), (2, 0))), + GriddedPerm((1, 0), ((2, 0), (2, 0))), + ), + requirements=( + ( + GriddedPerm((0,), ((1, 0),)), + GriddedPerm((0,), ((2, 0),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 1}), + ), + ) + ), + ), + ) + ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (3, 0))), + GriddedPerm((0, 1), ((3, 0), (3, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (3, 0))), + GriddedPerm((1, 0), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((2, 0), (3, 0))), + GriddedPerm((1, 0), ((3, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + (GriddedPerm((0,), ((3, 0),)),), + ), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (3, 0))), + GriddedPerm((0, 1), ((2, 0), (4, 0))), + GriddedPerm((0, 1), ((3, 0), (3, 0))), + GriddedPerm((0, 1), ((3, 0), (4, 0))), + GriddedPerm((0, 1), ((4, 0), (4, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (3, 0))), + GriddedPerm((1, 0), ((0, 0), (4, 0))), + GriddedPerm((1, 0), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((2, 0), (3, 0))), + GriddedPerm((1, 0), ((2, 0), (4, 0))), + GriddedPerm((1, 0), ((3, 0), (3, 0))), + GriddedPerm((1, 0), ((3, 0), (4, 0))), + GriddedPerm((1, 0), ((4, 0), (4, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (4, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (4, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + ( + GriddedPerm((0,), ((3, 0),)), + GriddedPerm((0,), ((4, 0),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 2, 3: 3, 4: 3}), + ), + ) + ), + ), + ) + ), # FusionStrategy(col_idx=1, tracked=True)( # Tiling( # obstructions=( @@ -116,29 +192,69 @@ # ), # ) # ), - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 1), (0, 1))), - # GriddedPerm((0, 1), ((0, 1), (0, 2))), - # GriddedPerm((0, 1), ((0, 2), (0, 2))), - # GriddedPerm((1, 0), ((0, 1), (0, 0))), - # GriddedPerm((1, 0), ((0, 1), (0, 1))), - # GriddedPerm((1, 0), ((0, 2), (0, 0))), - # GriddedPerm((1, 0), ((0, 2), (0, 1))), - # GriddedPerm((1, 0), ((0, 2), (0, 2))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), - # ), - # requirements=((GriddedPerm((0,), ((0, 1),)),),), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), - # ) - # ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((1, 0), ((0, 1), (0, 0))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 0))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 2))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 1),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((0, 1), ((0, 3), (0, 3))), + GriddedPerm((1, 0), ((0, 1), (0, 0))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 0))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 2))), + GriddedPerm((1, 0), ((0, 3), (0, 0))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((1, 0), ((0, 3), (0, 3))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 1),)), + GriddedPerm((0,), ((0, 2),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 1, 3: 2}, {0: 0}), + ), + ) + ), + ), + ) + ), FactorStrategy([[(0, 0)], [(1, 1)]])( Tiling( obstructions=( @@ -241,59 +357,140 @@ # ) # ) # )[1], - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((1, 0), (1, 0))), - # GriddedPerm((0, 1), ((2, 0), (2, 0))), - # GriddedPerm((0, 1), ((2, 0), (3, 0))), - # GriddedPerm((0, 1), ((3, 0), (3, 0))), - # GriddedPerm((1, 0), ((0, 0), (2, 0))), - # GriddedPerm((1, 0), ((0, 0), (3, 0))), - # GriddedPerm((1, 0), ((2, 0), (2, 0))), - # GriddedPerm((1, 0), ((2, 0), (3, 0))), - # GriddedPerm((1, 0), ((3, 0), (3, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), - # GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), - # ), - # requirements=( - # (GriddedPerm((0,), ((0, 0),)),), - # (GriddedPerm((0,), ((3, 0),)),), - # ), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((3, 0),)),)),), - # ) - # ), - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 1), (0, 1))), - # GriddedPerm((0, 1), ((0, 1), (0, 2))), - # GriddedPerm((0, 1), ((0, 2), (0, 2))), - # GriddedPerm((1, 0), ((0, 1), (0, 0))), - # GriddedPerm((1, 0), ((0, 1), (0, 1))), - # GriddedPerm((1, 0), ((0, 2), (0, 0))), - # GriddedPerm((1, 0), ((0, 2), (0, 1))), - # GriddedPerm((1, 0), ((0, 2), (0, 2))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), - # GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), - # ), - # requirements=((GriddedPerm((0,), ((0, 1),)),),), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 2),)),)),), - # ) - # ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((2, 0),))}), ignore_parent=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (3, 0))), + GriddedPerm((0, 1), ((3, 0), (3, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (3, 0))), + GriddedPerm((1, 0), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((2, 0), (3, 0))), + GriddedPerm((1, 0), ((3, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + (GriddedPerm((0,), ((3, 0),)),), + ), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (3, 0))), + GriddedPerm((0, 1), ((2, 0), (4, 0))), + GriddedPerm((0, 1), ((3, 0), (3, 0))), + GriddedPerm((0, 1), ((3, 0), (4, 0))), + GriddedPerm((0, 1), ((4, 0), (4, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (3, 0))), + GriddedPerm((1, 0), ((0, 0), (4, 0))), + GriddedPerm((1, 0), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((2, 0), (3, 0))), + GriddedPerm((1, 0), ((2, 0), (4, 0))), + GriddedPerm((1, 0), ((3, 0), (3, 0))), + GriddedPerm((1, 0), ((3, 0), (4, 0))), + GriddedPerm((1, 0), ((4, 0), (4, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (4, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (3, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (4, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((0, 0), (0, 0), (0, 0))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + ( + GriddedPerm((0,), ((3, 0),)), + GriddedPerm((0,), ((4, 0),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 2, 3: 3, 4: 3}), + ), + ) + ), + ), + ) + ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((0, 2),))}), ignore_parent=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((1, 0), ((0, 1), (0, 0))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 0))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 2))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 1),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((0, 1), ((0, 3), (0, 3))), + GriddedPerm((1, 0), ((0, 1), (0, 0))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 0))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 2))), + GriddedPerm((1, 0), ((0, 3), (0, 0))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((1, 0), ((0, 3), (0, 3))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 2))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((1, 2, 0), ((0, 0), (0, 0), (0, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 1),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 2}, {0: 0}), + ), + ) + ), + ), + ) + ), # FusionStrategy(row_idx=2, tracked=True)( # Tiling( # obstructions=( @@ -584,57 +781,197 @@ ), ) ), - # RequirementInsertionStrategy(gps=(GriddedPerm((0,), ((1, 0),)),),)( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 0), (0, 0))), - # GriddedPerm((0, 1), ((1, 0), (1, 0))), - # GriddedPerm((0, 1), ((0, 0), (1, 0))), - # GriddedPerm((1, 0), ((0, 0), (1, 0))), - # ), - # requirements=((GriddedPerm((0,), ((0, 0),)),),), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((1, 0),)),)),), - # ) - # ), - # RequirementInsertionStrategy( - # gps=frozenset({GriddedPerm((0,), ((1, 0),))}), ignore_parent=False - # )( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (1, 1))), - # GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 1))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (0, 1))), - # GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (1, 0))), - # GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), - # GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 0))), - # GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 1))), - # GriddedPerm((1, 0, 2), ((1, 1), (1, 0), (1, 1))), - # GriddedPerm((1, 0, 2), ((1, 1), (1, 1), (1, 1))), - # GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), - # GriddedPerm((2, 1, 0), ((1, 1), (1, 0), (1, 0))), - # GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 0))), - # GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 1))), - # GriddedPerm((0, 2, 1, 3), ((0, 0), (0, 0), (1, 0), (1, 0))), - # GriddedPerm((0, 2, 1, 3), ((0, 0), (0, 1), (0, 0), (0, 1))), - # GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (0, 1))), - # GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (1, 1))), - # GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (1, 1), (1, 1))), - # GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 1), (1, 1), (1, 1))), - # GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), - # GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (1, 1))), - # GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (1, 1), (1, 1))), - # ), - # requirements=( - # (GriddedPerm((0,), ((1, 0),)), GriddedPerm((0,), ((1, 1),))), - # ), - # assumptions=( - # TrackingAssumption( - # (GriddedPerm((0,), ((1, 0),)), GriddedPerm((0,), ((1, 1),))) - # ), - # ), - # ) - # ), + RequirementInsertionStrategy(gps=(GriddedPerm((0,), ((1, 0),)),),)( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((1, 0), ((0, 0), (1, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((0, 0), (2, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((1, 0), ((0, 0), (1, 0))), + GriddedPerm((1, 0), ((0, 0), (2, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 1}), + ), + ) + ), + ), + ) + ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((1, 0),))}), ignore_parent=False + )( + Tiling( + obstructions=( + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (1, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (1, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 0), (1, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 1), (1, 1))), + GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 0), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 1))), + GriddedPerm((0, 2, 1, 3), ((0, 0), (0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1, 3), ((0, 0), (0, 1), (0, 0), (0, 1))), + GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (1, 1))), + GriddedPerm((0, 2, 1, 3), ((0, 1), (0, 1), (1, 1), (1, 1))), + GriddedPerm((0, 3, 2, 1), ((0, 0), (0, 1), (1, 1), (1, 1))), + GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (1, 1))), + GriddedPerm((0, 3, 2, 1), ((0, 1), (0, 1), (1, 1), (1, 1))), + ), + requirements=( + (GriddedPerm((0,), ((1, 0),)), GriddedPerm((0,), ((1, 1),))), + ), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (1, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (2, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (2, 1))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (1, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 1), (2, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (1, 1))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (2, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (1, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((1, 0, 2), ((1, 0), (2, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 0), (1, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 1), (1, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (1, 1), (2, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (2, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((1, 1), (2, 1), (2, 1))), + GriddedPerm((1, 0, 2), ((2, 0), (2, 0), (2, 0))), + GriddedPerm((1, 0, 2), ((2, 0), (2, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((2, 1), (2, 0), (2, 1))), + GriddedPerm((1, 0, 2), ((2, 1), (2, 1), (2, 1))), + GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 0), (1, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 0), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (1, 1))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (1, 1), (2, 1))), + GriddedPerm((2, 1, 0), ((1, 1), (2, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (2, 1), (2, 0))), + GriddedPerm((2, 1, 0), ((1, 1), (2, 1), (2, 1))), + GriddedPerm((2, 1, 0), ((2, 0), (2, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((2, 1), (2, 0), (2, 0))), + GriddedPerm((2, 1, 0), ((2, 1), (2, 1), (2, 0))), + GriddedPerm((2, 1, 0), ((2, 1), (2, 1), (2, 1))), + GriddedPerm( + (0, 2, 1, 3), ((0, 0), (0, 0), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 0), (0, 0), (1, 0), (2, 0)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 0), (0, 0), (2, 0), (2, 0)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 0), (0, 1), (0, 0), (0, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (1, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (0, 1), (2, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (1, 1), (1, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (1, 1), (2, 1)) + ), + GriddedPerm( + (0, 2, 1, 3), ((0, 1), (0, 1), (2, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 0), (0, 1), (1, 1), (1, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 0), (0, 1), (1, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 0), (0, 1), (2, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (1, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (0, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (1, 1), (1, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (1, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 2, 1), ((0, 1), (0, 1), (2, 1), (2, 1)) + ), + ), + requirements=( + ( + GriddedPerm((0,), ((1, 0),)), + GriddedPerm((0,), ((1, 1),)), + GriddedPerm((0,), ((2, 0),)), + GriddedPerm((0,), ((2, 1),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1}, {0: 0, 1: 1, 2: 1}), + ), + ) + ), + ), + ) + ), ] equiv_rule_to_check = [r for r in rules_to_check if r.is_equivalence()] diff --git a/tilings/strategies/obstruction_inferral.py b/tilings/strategies/obstruction_inferral.py index 3e355eee..a4d6d946 100644 --- a/tilings/strategies/obstruction_inferral.py +++ b/tilings/strategies/obstruction_inferral.py @@ -47,14 +47,13 @@ def extra_parameters( raise StrategyDoesNotApply("Strategy does not apply") child = children[0] params: Dict[str, str] = {} - for assumption in comb_class.assumptions: - mapped_assumption = child.forward_map.map_assumption(assumption).avoiding( - child.obstructions - ) - if mapped_assumption.gps: - parent_var = comb_class.get_assumption_parameter(assumption) - child_var = child.get_assumption_parameter(mapped_assumption) - params[parent_var] = child_var + for parameter in comb_class.parameters: + mapped_parameter = parameter.add_obstructions_and_requirements( + self.gps, [] + ).apply_row_col_map(child.forward_map) + parent_var = comb_class.get_parameter_name(parameter) + child_var = child.get_parameter_name(mapped_parameter) + params[parent_var] = child_var return (params,) def backward_map( diff --git a/tilings/strategies/requirement_insertion.py b/tilings/strategies/requirement_insertion.py index 1c6753a4..22d309d6 100644 --- a/tilings/strategies/requirement_insertion.py +++ b/tilings/strategies/requirement_insertion.py @@ -83,20 +83,18 @@ def extra_parameters( av, co = children av_params: Dict[str, str] = {} co_params: Dict[str, str] = {} - for assumption in comb_class.assumptions: - parent_var = comb_class.get_assumption_parameter(assumption) - av_mapped_assumption = av.forward_map.map_assumption(assumption).avoiding( - av.obstructions - ) - if av_mapped_assumption.gps: - child_var = av.get_assumption_parameter(av_mapped_assumption) - av_params[parent_var] = child_var - co_mapped_assumption = co.forward_map.map_assumption(assumption).avoiding( - co.obstructions - ) - if co_mapped_assumption.gps: - child_var = co.get_assumption_parameter(co_mapped_assumption) - co_params[parent_var] = child_var + for parameter in comb_class.parameters: + parent_var = comb_class.get_parameter_name(parameter) + av_mapped_param = parameter.add_obstructions_and_requirements( + self.gps, [] + ).apply_row_col_map(av.forward_map) + child_var = av.get_parameter_name(av_mapped_param) + av_params[parent_var] = child_var + co_mapped_param = parameter.add_obstructions_and_requirements( + [], [self.gps] + ).apply_row_col_map(co.forward_map) + child_var = co.get_parameter_name(co_mapped_param) + co_params[parent_var] = child_var return av_params, co_params def __repr__(self) -> str: diff --git a/tox.ini b/tox.ini index cb507f83..d4801e83 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_inferral.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From 899e61ed8602280cb758ee545fa29e9149e13a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Tue, 2 Nov 2021 10:38:48 +0000 Subject: [PATCH 16/41] Remove unnecessary method redefintion (#377) * remove duplicated method * fix the initializer * remove prints --- tilings/map.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tilings/map.py b/tilings/map.py index eb097584..5e548d9b 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -209,7 +209,7 @@ def __init__( self._is_identity = is_identity super().__init__( { - cell: self.map_cell(cell) + cell: (col_map[cell[0]], row_map[cell[1]]) for cell in itertools.product(self._col_map, self._row_map) } ) @@ -273,19 +273,6 @@ def is_non_crossing(self) -> bool: return cols == sorted(cols) and rows == sorted(rows) # Mapping method - def is_mappable_cell(self, cell: Cell) -> bool: - """ - Return True if the cell can be mapped, i.e. if the image of the row - and the column of the are defined by the map. - """ - return self.is_mappable_col(cell[0]) and self.is_mappable_row(cell[1]) - - def map_cell(self, cell: Cell) -> Cell: - """ - Map the cell according to the map. - """ - return (self.map_col(cell[0]), self.map_row(cell[1])) - def is_mappable_row(self, row: int) -> bool: """ Return True if the image of the row is defined. From 6da840752482d14d6a48da78922f2a35e50153bc Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Tue, 2 Nov 2021 16:46:40 +0000 Subject: [PATCH 17/41] Factoring (#375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added a restricted_by and map_gps method to maps * added active region to preimages and parameters, and updated factor code * convert factoring sanity check * activate factor strategy tests * closer to right, but still wrong - needs more thinking. * reducible factorization is broken with params * get rid of sub_tiling method * map param properly * activate mypy and interleaving not supported * restricted_by -> restriction Co-authored-by: Émile Nadeau --- mypy.ini | 5 +- tests/strategies/test_sanity_check.py | 260 +++++++++++++++++++++----- tilings/algorithms/factor.py | 83 ++++---- tilings/map.py | 11 ++ tilings/parameter_counter.py | 56 +++++- tilings/strategies/factor.py | 69 ++----- tilings/tiling.py | 32 +--- tox.ini | 2 +- 8 files changed, 342 insertions(+), 176 deletions(-) diff --git a/mypy.ini b/mypy.ini index fb2e53f8..7c088cbb 100644 --- a/mypy.ini +++ b/mypy.ini @@ -30,4 +30,7 @@ ignore_errors = True ignore_errors = False [mypy-tilings.strategies.requirement_placement] -ignore_errors = False \ No newline at end of file +ignore_errors = False + +[mypy-tilings.strategies.factor] +ignore_errors = False diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index eef73e2d..d48c6ad2 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -277,53 +277,219 @@ ), ) ), - # FactorStrategy([[(0, 0)], [(1, 1)]])( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 0), (0, 0))), - # GriddedPerm((1, 0), ((1, 1), (1, 1))), - # ), - # assumptions=(TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)),), - # ) - # ), - # FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 0), (0, 0))), - # GriddedPerm((1, 0), ((1, 1), (1, 1))), - # GriddedPerm((0, 1), ((2, 2), (2, 2))), - # GriddedPerm((1, 0), ((2, 2), (2, 2))), - # ), - # requirements=( - # (GriddedPerm((0,), ((0, 0),)),), - # (GriddedPerm((0,), ((2, 2),)),), - # ), - # assumptions=( - # TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - # TrackingAssumption( - # (GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((2, 2),))), - # ), - # ), - # ), - # ), - # FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( - # Tiling( - # obstructions=( - # GriddedPerm((0, 1), ((0, 0), (0, 0))), - # GriddedPerm((1, 0), ((1, 1), (1, 1))), - # GriddedPerm((0, 1), ((2, 2), (2, 2))), - # GriddedPerm((1, 0), ((2, 2), (2, 2))), - # ), - # requirements=( - # (GriddedPerm((0,), ((0, 0),)),), - # (GriddedPerm((0,), ((2, 2),)),), - # ), - # assumptions=( - # TrackingAssumption((GriddedPerm((0,), ((0, 0),)),)), - # TrackingAssumption((GriddedPerm((0,), ((2, 2),)),)), - # ), - # ), - # ), + FactorStrategy([[(0, 0)], [(1, 1)]])( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + ), + requirements=(), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((1, 2), (1, 2))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1}, {0: 0, 1: 1}), + ), + ) + ), + ), + ) + ), + FactorStrategy([[(0, 0)], [(1, 1)]])( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + ), + requirements=(), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((1, 0), ((2, 1), (2, 1))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0, 1: 1}, {0: 0, 1: 0, 2: 1}), + ), + ) + ), + ), + ) + ), + FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((2, 2), (2, 2))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((2, 2), (2, 2))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + (GriddedPerm((0,), ((2, 2),)),), + ), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((2, 2), (2, 2))), + GriddedPerm((0, 1), ((2, 2), (2, 3))), + GriddedPerm((0, 1), ((2, 3), (2, 3))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((2, 2), (2, 2))), + GriddedPerm((1, 0), ((2, 3), (2, 2))), + GriddedPerm((1, 0), ((2, 3), (2, 3))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + ( + GriddedPerm((0,), ((2, 2),)), + GriddedPerm((0,), ((2, 3),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 2}, {0: 0, 1: 1, 2: 2}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((2, 3), (2, 3))), + GriddedPerm((1, 0), ((1, 2), (1, 2))), + GriddedPerm((1, 0), ((2, 3), (2, 3))), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((0, 1),)), + ), + (GriddedPerm((0,), ((2, 3),)),), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1, 3: 2}, {0: 0, 1: 1, 2: 2}), + ), + ) + ), + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (0, 1))), + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((2, 3), (2, 3))), + GriddedPerm((1, 0), ((1, 2), (1, 2))), + GriddedPerm((1, 0), ((2, 3), (2, 3))), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((0, 1),)), + ), + (GriddedPerm((0,), ((2, 3),)),), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1, 3: 2}, {0: 0, 1: 1, 2: 2}), + ), + ) + ), + ), + ) + ), + FactorStrategy([[(0, 0)], [(1, 1)], [(2, 2)]])( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((2, 2), (2, 2))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((2, 2), (2, 2))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + (GriddedPerm((0,), ((2, 2),)),), + ), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((2, 2), (2, 2))), + GriddedPerm((0, 1), ((2, 2), (3, 2))), + GriddedPerm((0, 1), ((3, 2), (3, 2))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((2, 2), (2, 2))), + GriddedPerm((1, 0), ((2, 2), (3, 2))), + GriddedPerm((1, 0), ((3, 2), (3, 2))), + ), + requirements=( + (GriddedPerm((0,), ((0, 0),)),), + ( + GriddedPerm((0,), ((2, 2),)), + GriddedPerm((0,), ((3, 2),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2}, {0: 0, 1: 1, 2: 2, 3: 2}), + ), + ) + ), + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((3, 2), (3, 2))), + GriddedPerm((1, 0), ((2, 1), (2, 1))), + GriddedPerm((1, 0), ((3, 2), (3, 2))), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((1, 0),)), + ), + (GriddedPerm((0,), ((3, 2),)),), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2}, {0: 0, 1: 0, 2: 1, 3: 2}), + ), + ) + ), + ), + ) + ), # list( # SlidingFactory(use_symmetries=True)( # Tiling( diff --git a/tilings/algorithms/factor.py b/tilings/algorithms/factor.py index a02bf133..02b6e3a4 100644 --- a/tilings/algorithms/factor.py +++ b/tilings/algorithms/factor.py @@ -5,14 +5,17 @@ from permuta.misc import UnionFind from tilings.griddedperm import GriddedPerm from tilings.misc import partitions_iterator -from tilings.parameter_counter import ParameterCounter if TYPE_CHECKING: from tilings import Tiling + from tilings.parameter_counter import ParameterCounter Cell = Tuple[int, int] ReqList = Tuple[GriddedPerm, ...] +UninitializedTiling = Tuple[ + Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple["ParameterCounter", ...] +] class Factor: @@ -32,15 +35,7 @@ def __init__(self, tiling: "Tiling") -> None: 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[ParameterCounter, ...], - ] - ] - ] = None + self._factors_obs_and_reqs: Optional[List[UninitializedTiling]] = None def _cell_to_int(self, cell: Cell) -> int: nrow = self._tiling.dimensions[1] @@ -75,8 +70,9 @@ def _unite_parameters(self) -> None: """ Unite according to parameters. """ - if self._tiling.parameters: - raise NotImplementedError + for param in self._tiling.parameters: + for cells in param.active_regions(self._tiling): + self._unite_cells(cells) def _unite_obstructions(self) -> None: """ @@ -138,38 +134,42 @@ def get_components(self) -> Tuple[Set[Cell], ...]: self._components = tuple(all_components.values()) return self._components + def _get_factor_obs_and_reqs(self, component: Set[Cell]) -> UninitializedTiling: + """ + Builds the obstructions, requirements and parameters of the component. + """ + obstructions = tuple( + ob for ob in self._tiling.obstructions if ob.pos[0] in component + ) + requirements = self._tiling.sort_requirements( + req for req in self._tiling.requirements if req[0].pos[0] in component + ) + parameters = sorted( + set( + param.sub_param(component, self._tiling) + for param in self._tiling.parameters + ) + ) + return ( + obstructions, + requirements, + tuple(param for param in parameters if param.counters), + ) + def _get_factors_obs_and_reqs( self, - ) -> List[ - Tuple[ - Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple[ParameterCounter, ...] - ], - ]: + ) -> List[UninitializedTiling]: """ 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 if self._tiling.is_empty(): return [((GriddedPerm((), []),), tuple(), tuple())] - 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 - ) - assert not self._tiling.parameters - parameters: Tuple[ParameterCounter, ...] = tuple() - factors.append( - ( - obstructions, - requirements, - parameters, - ) - ) + factors = [ + self._get_factor_obs_and_reqs(component) + for component in self.get_components() + ] self._factors_obs_and_reqs = factors return self._factors_obs_and_reqs @@ -179,6 +179,16 @@ def factorable(self) -> bool: """ return len(self.get_components()) > 1 + def factor(self, component: Set[Cell]) -> "Tiling": + """ + Build the factor for the given component. + """ + return self._tiling.__class__( + *self._get_factor_obs_and_reqs(component), + simplify=False, + sorted_input=True, + ) + def factors(self) -> Tuple["Tiling", ...]: """ Returns all the irreducible factors of the tiling. @@ -203,6 +213,9 @@ def reducible_factorisations(self) -> Iterator[Tuple["Tiling", ...]]: For example if T = T1 x T2 x T3 then (T1 x T3) x T2 is a possible reducible factorisation. """ + if self._tiling.parameters: + # The parameter needs to be grouped back togheter. + raise NotImplementedError min_comp = self._get_factors_obs_and_reqs() for partition in partitions_iterator(min_comp): factors = [] diff --git a/tilings/map.py b/tilings/map.py index 5e548d9b..6c4f88af 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -3,10 +3,12 @@ TYPE_CHECKING, Callable, Dict, + FrozenSet, Iterable, Iterator, List, Optional, + Set, Tuple, ) @@ -45,6 +47,12 @@ def inverse(self) -> "CellMap": raise InvalidOperationError("The map is not reversible.") return CellMap(inverse_map) + def restriction(self, cells: Set[Cell]): + """ + Return the cell map where the domain is restricted to cells. + """ + return CellMap({a: b for a, b in self._map.items() if a in cells}) + def to_row_col_map(self) -> "RowColMap": """ Convert the CellMap object into an equivalent RowColMap object. @@ -161,6 +169,9 @@ def map_gp(self, gp: "GriddedPerm") -> "GriddedPerm": """ return gp.__class__(gp.patt, map(self.map_cell, gp.pos)) + def map_gps(self, gps: Iterable["GriddedPerm"]) -> FrozenSet["GriddedPerm"]: + return frozenset(self.map_gp(gp) for gp in gps) + def is_mappable_cell(self, cell: Cell) -> bool: """ Return True if the cell can be mapped. diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 35c02439..fca66e16 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -1,6 +1,7 @@ import itertools -from typing import TYPE_CHECKING, Iterable, Iterator, Tuple +from typing import TYPE_CHECKING, Iterable, Iterator, List, Set, Tuple +from .algorithms.factor import Factor from .griddedperm import GriddedPerm from .map import RowColMap @@ -53,6 +54,41 @@ def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": self.map = self.map.compose(row_col_map) return self + def active_region(self, tiling: "Tiling") -> Set[Cell]: + """ + Yield the active region of the preimage counter. + """ + res = set() + for cell in self.tiling.active_cells: + if sum(1 for _ in self.map.preimage_cell(cell)) > 1: + res.add(cell) + extra_obs, extra_reqs = self.extra_obs_and_reqs(tiling) + for gp in itertools.chain(extra_obs, *extra_reqs): + res.update(gp.pos) + return res + + def sub_preimage(self, cells: Set[Cell]) -> "PreimageCounter": + precells = set( + itertools.chain.from_iterable( + self.map.preimage_cell(cell) for cell in cells + ) + ) + sub_tiling = Factor(self.tiling).factor(precells) + sub_map = self.map.restriction(precells) + return PreimageCounter(sub_tiling, sub_map.to_row_col_map()) + + def extra_obs_and_reqs( + self, tiling: "Tiling" + ) -> Tuple[List[GriddedPerm], List[Tuple[GriddedPerm, ...]]]: + extra_obs, extra_reqs = [], [] + for ob in self.tiling.obstructions: + if self.map.map_gp(ob) not in tiling.obstructions: + extra_obs.append(ob) + for req in self.tiling.requirements: + if tuple(sorted(self.map.map_gps(req))) not in tiling.requirements: + extra_reqs.append(req) + return extra_obs, extra_reqs + def num_preimage(self, gp: GriddedPerm) -> int: """ Return the number of preimage for the given gridded permutation. @@ -123,11 +159,23 @@ class ParameterCounter: def __init__(self, counters: Iterable[PreimageCounter]): self.counters = tuple(sorted(counters)) - def active_region(self) -> Iterator[Cell]: + def active_regions(self, tiling: "Tiling") -> Iterator[Set[Cell]]: """ - Return the region of the assumption considered active. + Yield the active regions of the preimage counters. """ - raise NotImplementedError + for preimage in self: + yield set( + preimage.map.map_cell(cell) for cell in preimage.active_region(tiling) + ) + + def sub_param( + self, cells: Set[Cell], underlying_tiling: "Tiling" + ) -> "ParameterCounter": + res = [] + for preimage in self.counters: + if preimage.active_region(underlying_tiling) <= cells: + res.append(preimage.sub_preimage(cells)) + return ParameterCounter(res) def get_value(self, gp: GriddedPerm) -> int: """ diff --git a/tilings/strategies/factor.py b/tilings/strategies/factor.py index 675e358f..5a5c2ba9 100644 --- a/tilings/strategies/factor.py +++ b/tilings/strategies/factor.py @@ -55,7 +55,8 @@ def __init__( ) def decomposition_function(self, tiling: Tiling) -> Tuple[Tiling, ...]: - return tuple(tiling.sub_tiling(cells) for cells in self.partition) + factor_algo = Factor(tiling) + return tuple(factor_algo.factor(set(cells)) for cells in self.partition) def extra_parameters( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None @@ -68,11 +69,13 @@ def extra_parameters( for parent_var, parameter in zip( comb_class.extra_parameters, comb_class.parameters ): - for idx, child in enumerate(children): - # TODO: consider skew/sum - new_parameter = child.forward_map.map_param(parameter) - child_var = child.get_parameter_name(new_parameter) - extra_parameters[idx][parent_var] = child_var + for idx, (component, child) in enumerate(zip(self.partition, children)): + new_parameter = parameter.sub_param( + set(component), comb_class + ).apply_row_col_map(child.forward_map) + if new_parameter.counters: + child_var = child.get_parameter_name(new_parameter) + extra_parameters[idx][parent_var] = child_var return extra_parameters def formal_step(self) -> str: @@ -183,19 +186,7 @@ def assumptions_to_add( Return the assumptions that should be tracked in the set of cells if we are interleaving the given rows and cols. """ - col_assumptions = [ - TrackingAssumption( - [GriddedPerm.point_perm(cell) for cell in cells if x == cell[0]] - ) - for x in cols - ] - row_assumptions = [ - TrackingAssumption( - [GriddedPerm.point_perm(cell) for cell in cells if y == cell[1]] - ) - for y in rows - ] - return tuple(ass for ass in chain(col_assumptions, row_assumptions) if ass.gps) + raise NotImplementedError("Don't know what to do with the preimage") def contains_interleaving_assumptions( @@ -206,13 +197,7 @@ def contains_interleaving_assumptions( assumptions needed to count the interleavings, and therefore the children too. """ - cols, rows = interleaving_rows_and_cols(partition) - return all( - ass in comb_class.assumptions - for ass in chain.from_iterable( - assumptions_to_add(cells, cols, rows) for cells in partition - ) - ) + raise NotImplementedError("Don't know what to do with the preimage") class Interleaving(CartesianProduct[Tiling, GriddedPerm]): @@ -297,37 +282,7 @@ def interleaving_parameters(self, comb_class: Tiling) -> List[Tuple[str, ...]]: """ Return the parameters on the parent tiling that needed to be interleaved. """ - res: List[Tuple[str, ...]] = [] - cols, rows = interleaving_rows_and_cols(self.partition) - for x in cols: - assumptions = [ - TrackingAssumption( - GriddedPerm.point_perm(cell) for cell in cells if x == cell[0] - ) - for cells in self.partition - ] - res.append( - tuple( - comb_class.get_assumption_parameter(ass) - for ass in assumptions - if ass.gps - ) - ) - for y in rows: - assumptions = [ - TrackingAssumption( - GriddedPerm.point_perm(cell) for cell in cells if y == cell[1] - ) - for cells in self.partition - ] - res.append( - tuple( - comb_class.get_assumption_parameter(ass) - for ass in assumptions - if ass.gps - ) - ) - return res + raise NotImplementedError def backward_map( self, diff --git a/tilings/tiling.py b/tilings/tiling.py index 4de21d7d..7396f987 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -1029,36 +1029,6 @@ def component_fusion(self, row=None, col=None): """ raise NotImplementedError("Update to use general fusion algorithm.") - def sub_tiling( - self, - cells: Iterable[Cell], - factors: bool = False, - add_parameters: Iterable[ParameterCounter] = tuple(), - ) -> "Tiling": - """Return the tiling using only the obstructions and requirements - completely contained in the given cells. If factors is set to True, - then it assumes that the first cells confirms if a gridded perm uses only - the cells.""" - obstructions = tuple( - ob - for ob in self.obstructions - if (factors and ob.pos[0] in cells) or all(c in cells for c in ob.pos) - ) - requirements = Tiling.sort_requirements( - req - for req in self.requirements - if (factors and req[0].pos[0] in cells) - or all(c in cells for c in chain.from_iterable(r.pos for r in req)) - ) - if self._parameters: - raise NotImplementedError - return self.__class__( - obstructions, - requirements, - simplify=False, - sorted_input=True, - ) - def find_factors(self, interleaving: str = "none") -> Tuple["Tiling", ...]: """ Return list with the factors of the tiling. @@ -1238,7 +1208,7 @@ def _handle_html_parameter(self, result: List[str], style) -> List[str]: ] has_param: Dict[int, List[str]] = {} for c, param_counter in enumerate(self.parameters): - for i, j in param_counter.active_region(): + for i, j in set(chain(*param_counter.active_regions(self))): dim_i, dim_j = self.dimensions index = (dim_j - j - 1) * (3 * dim_i + 2) + i * 3 + 2 if c >= len(colors): diff --git a/tox.ini b/tox.ini index d4801e83..cdcd656d 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_factors.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From a1d38a957ab7936564c9d6c0d2b2ac8ab9389dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 11 Nov 2021 11:12:05 +0000 Subject: [PATCH 18/41] display (#380) * make the row_map and col_map public via property * use displayer class to build tiling string * special symbol for empty tiling * move html code to display algorithm * fix pylint * special case extra obs and req for the empty case. --- tilings/algorithms/__init__.py | 2 + tilings/algorithms/display.py | 225 +++++++++++++++++++++++++++++++++ tilings/map.py | 8 ++ tilings/parameter_counter.py | 2 + tilings/tiling.py | 194 +--------------------------- 5 files changed, 240 insertions(+), 191 deletions(-) create mode 100644 tilings/algorithms/display.py diff --git a/tilings/algorithms/__init__.py b/tilings/algorithms/__init__.py index d72c23a6..cda54572 100644 --- a/tilings/algorithms/__init__.py +++ b/tilings/algorithms/__init__.py @@ -1,3 +1,4 @@ +from .display import TilingDisplayer from .enumeration import DatabaseEnumeration, LocalEnumeration, MonotoneTreeEnumeration from .factor import Factor, FactorWithInterleaving, FactorWithMonotoneInterleaving from .fusion import Fusion @@ -36,4 +37,5 @@ "SubclassVerificationAlgorithm", "guess_obstructions", "Sliding", + "TilingDisplayer", ] diff --git a/tilings/algorithms/display.py b/tilings/algorithms/display.py new file mode 100644 index 00000000..6c4ded36 --- /dev/null +++ b/tilings/algorithms/display.py @@ -0,0 +1,225 @@ +import itertools +from collections import defaultdict +from string import ascii_uppercase +from typing import TYPE_CHECKING, Dict, FrozenSet, Iterable, Iterator, List, Set, Tuple + +from permuta import Perm + +if TYPE_CHECKING: + from tilings import GriddedPerm, Tiling + from tilings.parameter_counter import PreimageCounter + +__all__ = ["TilingDisplayer"] + +Cell = Tuple[int, int] +POINT_BASIS = frozenset([Perm((0, 1)), Perm((1, 0))]) +PARAM_COLORS = ( + "#b0dbff", + "#d1f0af", + "#db8686", + "#FCC997", + "#b0ffd0", + "#FCEB97", + "#fc97b4", + "#4b45ff", + "#c8bdff", + "#bfbfbf", +) +TILING_HTML_STYLE = ( + """border: 1px solid; width: 24px; height: 24px; text-align: center;""" +) + + +def words_generator(alphabet: str) -> Iterator[str]: + """ + Iterator on word over the alphabet in lexicographic order. + """ + length = 1 + while True: + yield from map("".join, itertools.product(alphabet, repeat=length)) + length += 1 + + +class TilingDisplayer: + LABELS: Dict[FrozenSet[Perm], str] = { + frozenset([Perm()]): "\u03b5", + frozenset([Perm((0,))]): " ", + POINT_BASIS: "\u25cb", + frozenset([Perm((0, 1))]): "\\", + frozenset([Perm((1, 0))]): "/", + } + LABEL_ITERATOR = words_generator(ascii_uppercase) + + def __init__(self, tiling: "Tiling"): + self.tiling = tiling + self.label_used: Set[FrozenSet[Perm]] = set() + + def ascii(self) -> str: + lines = self.grid_lines(self.tiling) + lines.extend(self.legend()) + lines.extend(self.crossing_obs_lines(self.tiling.obstructions)) + lines.extend(self.req_lines(self.tiling.requirements)) + lines.extend(self.params_lines()) + return "\n".join(lines) + + def html(self) -> str: + """Returns an html representation of the tilings object""" + grid = self.grid(self.tiling) + param_dict = self.cell_param() + result = [] + # Create tiling html table + result.append(" ") + for rev_row_idx, row in enumerate(grid): + row_idx = self.tiling.dimensions[1] - 1 - rev_row_idx + result.append("") + for col_idx, label in enumerate(row): + cell = (col_idx, row_idx) + cell_style = self.cell_background_style(param_dict[cell]) + result.append(f"") + result.append("") + result.append("
") + result.append(label) + result.append("
") + return "".join(result) + + def get_label(self, basis: Iterable[Perm], positive: bool) -> str: + """ + Return the appropriate label of the basis + """ + basis = frozenset(basis) + self.label_used.add(basis) + if basis not in self.LABELS: + self.LABELS[basis] = next(self.LABEL_ITERATOR) + label = self.LABELS[basis] + if positive: + if basis == POINT_BASIS: + label = "\u25cf" + else: + label += "+" + return label + + def legend(self) -> List[str]: + content = [] + for basis in self.label_used: + label = self.LABELS[basis] + if label[0] in ascii_uppercase: + content.append(f"{label}: Av({', '.join(map(str, basis))})") + content.sort() + return content + + def grid(self, tiling: "Tiling") -> List[List[str]]: + grid = [ + ["" for _ in range(tiling.dimensions[0])] + for _ in range(tiling.dimensions[1]) + ] + for cell, (basis, _) in sorted(tiling.cell_basis().items()): + label = self.get_label(basis, cell in tiling.positive_cells) + grid[-1 - cell[1]][cell[0]] = label + return grid + + def grid_lines(self, tiling: "Tiling") -> List[str]: + """ + Compute the grid that represents the given tiling. + """ + grid = self.grid(tiling) + col_widths = [ + max(len(row[i]) for row in grid) for i in range(tiling.dimensions[0]) + ] + grid = [ + [ + label + " " * (width - len(label)) + for label, width in zip(row, col_widths) + ] + for row in grid + ] + horizontal_line = f"+{'+'.join('-' * width for width in col_widths)}+" + lines = [horizontal_line] + for row in grid: + lines.append(f"|{'|'.join(row)}|") + lines.append(horizontal_line) + return lines + + @staticmethod + def crossing_obs_lines(obs: Iterable["GriddedPerm"]) -> List[str]: + lines = [] + for ob in obs: + if not ob.is_single_cell(): + lines.append(str(ob)) + if lines: + lines = ["Crossing obstructions:"] + lines + return lines + + @staticmethod + def req_lines(reqs: Iterable[Iterable["GriddedPerm"]]) -> List[str]: + lines = [] + for i, req in enumerate(reqs): + lines.append(f"Requirement {i}:") + for r in req: + lines.append(str(r)) + return lines + + def params_lines(self) -> List[str]: + lines = [] + for i, param in enumerate(self.tiling.parameters): + lines.append(f"** Parameter {i} **") + for preimage_counter in param: + lines.extend( + self.indent(self.preimage_counter_lines(preimage_counter), 2) + ) + return lines + + def preimage_counter_lines(self, preimage: "PreimageCounter") -> List[str]: + lines = [] + if any(k != v for k, v in preimage.map.row_map.items()): + lines.append(f"row map: {self.map_str(preimage.map.row_map)}") + if any(k != v for k, v in preimage.map.col_map.items()): + lines.append(f"col map: {self.map_str(preimage.map.col_map)}") + lines.extend(self.grid_lines(preimage.tiling)) + extra_obs, extra_reqs = preimage.extra_obs_and_reqs(self.tiling) + lines.extend(self.crossing_obs_lines(extra_obs)) + lines.extend(self.req_lines(extra_reqs)) + return lines + + def cell_param(self) -> Dict[Cell, List[int]]: + """ + Return a dict with the index of all the param touching each cell. + """ + res: Dict[Cell, List[int]] = defaultdict(list) + for index, param_counter in enumerate(self.tiling.parameters): + active_region = set( + itertools.chain(*param_counter.active_regions(self.tiling)) + ) + for cell in active_region: + res[cell].append(index) + return res + + @staticmethod + def cell_background_style(params: List[int]) -> str: + if not params: + return "" + if max(params) >= len(PARAM_COLORS) or len(params) > 4: + # display gray lines if out of color or + # more than 4 parameters in single cell + return """background-image: + repeating-linear-gradient( + 45deg, #ffffff, #ffffff 6px, #00000080 1px, #00000080 7px + );""" + background_image = "background-image: linear-gradient(180deg" + stripe_size = 24 // len(params) + for idx, color in enumerate(map(PARAM_COLORS.__getitem__, params)): + background_image += f",{color} {idx*stripe_size}px, " + background_image += f"{color} {(idx+1)*stripe_size}px" + background_image += ");" + return background_image + + @staticmethod + def map_str(d: dict) -> str: + content = ", ".join(f"{k}:{v}" for k, v in d.items()) + return f"{{{content}}}" + + @staticmethod + def indent(lines: List[str], space: int) -> List[str]: + """ + Indent all the given line by the given amount of withe space. + """ + return [" " * space + line for line in lines] diff --git a/tilings/map.py b/tilings/map.py index 962d03e6..f43f3182 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -225,6 +225,14 @@ def __init__( } ) + @property + def row_map(self) -> Dict[int, int]: + return self._row_map + + @property + def col_map(self) -> Dict[int, int]: + return self._col_map + @classmethod def identity(cls, dimensions: Tuple[int, int]) -> "RowColMap": """ diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index fca66e16..c59f05db 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -81,6 +81,8 @@ def extra_obs_and_reqs( self, tiling: "Tiling" ) -> Tuple[List[GriddedPerm], List[Tuple[GriddedPerm, ...]]]: extra_obs, extra_reqs = [], [] + if self.tiling == self.tiling.__class__(): + return ([], []) for ob in self.tiling.obstructions: if self.map.map_gp(ob) not in tiling.obstructions: extra_obs.append(ob) diff --git a/tilings/tiling.py b/tilings/tiling.py index e771a3ed..231ddcf9 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -41,6 +41,7 @@ RowColSeparation, SubclassVerificationAlgorithm, SubobstructionInferral, + TilingDisplayer, guess_obstructions, ) from .exception import InvalidOperationError @@ -1187,110 +1188,9 @@ def is_subclass(self, perms_to_check: Iterable[Perm]): # HTML methods # ------------------------------------------------------------- - def _handle_html_parameter(self, result: List[str], style) -> List[str]: - """adds background color in cells where parameter happens""" - # pylint: disable=too-many-locals - colors = [ - "#b0dbff", - "#d1f0af", - "#db8686", - "#FCC997", - "#b0ffd0", - "#FCEB97", - "#fc97b4", - "#4b45ff", - "#c8bdff", - "#bfbfbf", - ] - has_param: Dict[int, List[str]] = {} - for c, param_counter in enumerate(self.parameters): - for i, j in set(chain(*param_counter.active_regions(self))): - dim_i, dim_j = self.dimensions - index = (dim_j - j - 1) * (3 * dim_i + 2) + i * 3 + 2 - if c >= len(colors): - pass - elif index in has_param: - has_param[index].append(colors[c]) - else: - has_param[index] = [colors[c]] - - if c >= len(colors) or len(has_param[index]) > 4: - # display gray lines if out of color or - # more than 4 parameters in single cell - background_image = """background-image: - repeating-linear-gradient( - 45deg, #ffffff, #ffffff 6px, #00000080 1px, #00000080 7px - );""" - else: - # display stripes - background_image = "background-image: linear-gradient(180deg" - stripe_size = 24 // len(has_param[index]) - for idx, color in enumerate(has_param[index]): - background_image += f""", - {color} {idx*stripe_size}px, - {color} {(idx+1)*stripe_size}px""" - background_image += ");" - result[index] = f'' - return result - def to_html_representation(self) -> str: """Returns an html representation of the tilings object""" - # pylint: disable=too-many-locals - # stylesheet for tiling - style = """ - border: 1px solid; - width: 24px; - height: 24px; - text-align: center; - """ - dim_i, dim_j = self.dimensions - result = [] - # Create tiling html table - result.append(" ") - for _ in range(dim_j): - result.append("") - for _ in range(dim_i): - result.append(f"") - result.append("") - result.append("
") - result.append(" ") - result.append("
") - labels: Dict[Tuple[Tuple[Perm, ...], bool], str] = {} - - # Put the sets in the tiles - - # How many characters are in a row in the grid - row_width = 3 * dim_i + 2 - curr_label = 1 - for cell, gridded_perms in sorted(self.cell_basis().items()): - obstructions, _ = gridded_perms - basis = list(sorted(obstructions)) - if basis == [Perm((0,))]: - continue - # the block, is the basis and whether or not positive - block = (tuple(basis), cell in self.positive_cells) - label = labels.get(block) - if label is None: - if basis == [Perm((0, 1)), Perm((1, 0))]: - if cell in self.positive_cells: - label = "\u25cf" - else: - label = "\u25cb" - elif basis == [Perm((0, 1))]: - label = "\\" - elif basis == [Perm((1, 0))]: - label = "/" - else: - label = str(curr_label) - curr_label += 1 - labels[block] = label - row_index_from_top = dim_j - cell[1] - 1 - index = row_index_from_top * row_width + cell[0] * 3 + 3 - result[index] = label - - # adds background color in cells where parameter happens - result = self._handle_html_parameter(result, style) - return "".join(result) + return TilingDisplayer(self).html() # ------------------------------------------------------------- # Properties and getters @@ -1765,92 +1665,4 @@ def __repr__(self) -> str: ) def __str__(self) -> str: - # pylint: disable=too-many-locals - # pylint: disable=too-many-branches - # pylint: disable=too-many-statements - dim_i, dim_j = self.dimensions - result = [] - # Create tiling lines - for j in range(2 * dim_j + 1): - for i in range(2 * dim_i + 1): - # Whether or not a vertical line and a horizontal line is - # present - vertical = i % 2 == 0 - horizontal = j % 2 == 0 - if vertical: - if horizontal: - result.append("+") - else: - result.append("|") - elif horizontal: - result.append("-") - else: - result.append(" ") - result.append("\n") - - labels: Dict[Tuple[Tuple[Perm, ...], bool], str] = {} - - # Put the sets in the tiles - - # How many characters are in a row in the grid - row_width = 2 * dim_i + 2 - curr_label = 1 - for cell, gridded_perms in sorted(self.cell_basis().items()): - obstructions, _ = gridded_perms - basis = list(sorted(obstructions)) - if basis == [Perm((0,))]: - continue - # the block, is the basis and whether or not positive - block = (tuple(basis), cell in self.positive_cells) - label = labels.get(block) - if label is None: - if basis == [Perm((0, 1)), Perm((1, 0))]: - if cell in self.positive_cells: - label = "\u25cf" - else: - label = "\u25cb" - elif basis == [Perm((0, 1))]: - label = "\\" - elif basis == [Perm((1, 0))]: - label = "/" - else: - label = str(curr_label) - curr_label += 1 - labels[block] = label - row_index_from_top = dim_j - cell[1] - 1 - index = (2 * row_index_from_top + 1) * row_width + 2 * cell[0] + 1 - result[index] = label - - # Legend at bottom - for block, label in sorted(labels.items(), key=lambda x: x[1]): - basis_el, positive = block - result.append(label) - result.append(": ") - if basis_el == (Perm((0, 1)), Perm((1, 0))) and positive: - result.append("point") - else: - result.append( - f"Av{'+' if positive else ''}" - f"({', '.join(str(p) for p in basis_el)})" - ) - result.append("\n") - - if any(not ob.is_single_cell() for ob in self.obstructions): - result.append("Crossing obstructions:\n") - for ob in self.obstructions: - if not ob.is_single_cell(): - result.append(str(ob)) - result.append("\n") - for i, req in enumerate(self.requirements): - result.append(f"Requirement {i}:\n") - for r in req: - result.append(str(r)) - result.append("\n") - for i, param in enumerate(self.parameters): - result.append(f"Parameter {i}:\n") - result.append(str(param)) - result.append("\n") - if self.parameters or self.requirements: - result = result[:-1] - - return "".join(result) + return TilingDisplayer(self).ascii() From 0640414a8f1fddb2144581cf7e23bc5332ca846e Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 11 Nov 2021 12:23:30 +0000 Subject: [PATCH 19/41] overwriting/deleting old fusion (#378) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * overwriting/deleting old fusion * fix factor bug * outline for fusing preimages * general fusion now just a test file to be removed later * add a first crack at a fusable method * add the remove identity preimage strategy * _row_map -> row_map * mypy for fusion * Revert "mypy for fusion" This reverts commit cda521819e25d2c185f44bb8726670982ecaef7d. * fix argument name to match parent class * remove empty preimages from parameter counter Co-authored-by: Émile Nadeau --- mypy.ini | 3 + tilings/algorithms/fusion.py | 461 ++++++--------------- tilings/algorithms/general_fusion.py | 392 ------------------ tilings/parameter_counter.py | 12 +- tilings/strategies/parameter_strategies.py | 76 ++++ 5 files changed, 216 insertions(+), 728 deletions(-) delete mode 100644 tilings/algorithms/general_fusion.py create mode 100644 tilings/strategies/parameter_strategies.py diff --git a/mypy.ini b/mypy.ini index 7c088cbb..3f150d27 100644 --- a/mypy.ini +++ b/mypy.ini @@ -34,3 +34,6 @@ ignore_errors = False [mypy-tilings.strategies.factor] ignore_errors = False + +[mypy-tilings.strategies.parameter_strategies] +ignore_errors = False diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index a1e45a67..72ab2ad9 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -1,37 +1,25 @@ """ -The implementation of the fusion algorithm + The implementation of the fusion algorithm """ -import collections -from typing import TYPE_CHECKING, Counter, Iterable, Iterator, List, Optional, Tuple -from tilings.assumptions import TrackingAssumption +from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple + from tilings.griddedperm import GriddedPerm +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter if TYPE_CHECKING: from tilings import Tiling + Cell = Tuple[int, int] +ReqList = Tuple[GriddedPerm, ...] +UninitializedTiling = Tuple[ + Tuple[GriddedPerm, ...], Tuple[ReqList, ...], Tuple["ParameterCounter", ...] +] class Fusion: - """ - Fusion algorithm container class. - - Check if a fusion is valid and compute the fused tiling. - - If `row_idx` is provided it attempts to fuse row `row_idx` with row - `row_idx+1`. - - If incited `col_ids` is provided it attempts to fuse column `col_idx` with - column `col_idx+1`. - - Isolation Levels: - None: no restrictions - "noninteracting": there can be at most one assumption involving the cells in the - fused rows/cols - "isolated": there can be no assumptions except the one induced by the fusion - """ - def __init__( self, tiling: "Tiling", @@ -40,13 +28,10 @@ def __init__( tracked: bool = False, isolation_level: Optional[str] = None, ): - self._tiling: "Tiling" = tiling - self._assumptions_fuse_counters: Optional[List[Counter[GriddedPerm]]] = None - self._obstruction_fuse_counter: Optional[Counter[GriddedPerm]] = None - self._requirements_fuse_counters: Optional[List[Counter[GriddedPerm]]] = None - self._tracked = tracked # add a TrackingAssumption to the region being tracked. - self._positive_left = False - self._positive_right = False + if isolation_level is not None: + raise NotImplementedError("Good luck Jay!") + self.tiling = tiling + self.tracked = tracked if row_idx is None and col_idx is not None: self._col_idx = col_idx self._fuse_row = False @@ -55,341 +40,149 @@ def __init__( self._fuse_row = True else: raise RuntimeError("Cannot specify a row and a columns") - self.isolation_level = isolation_level - assert self.isolation_level in [ - None, - "noninteracting", - "isolated", - ], "The only valid isolation levels are None, 'noninteracting', and 'isolated'." self._fused_tiling: Optional["Tiling"] = None + self.fuse_map = self._fuse_map() - def fuse_gridded_perm(self, gp: GriddedPerm) -> GriddedPerm: - """ - Fuse the gridded permutation `gp`. - """ - fused_pos = [] - for x, y in gp.pos: - if self._fuse_row and y > self._row_idx: - y -= 1 - elif not self._fuse_row and x > self._col_idx: - x -= 1 - fused_pos.append((x, y)) - return gp.__class__(gp.patt, fused_pos) - - def unfuse_gridded_perm( - self, gp: GriddedPerm, left_points: Optional[int] = None - ) -> Iterator[GriddedPerm]: - """ - Generator of all the possible ways to unfuse a gridded permutations. - - If left_points is given, the iterator contains only one gridded - permutations with said number of left points. - """ - - def stretch_above(p): - return p if p[1] < self._row_idx else (p[0], p[1] + 1) - - def stretch_left(p): - return p if p[0] < self._col_idx else (p[0] + 1, p[1]) - + def _fuse_map(self) -> RowColMap: + num_col, num_row = self.tiling.dimensions + row_map = {i: i for i in range(num_row)} if self._fuse_row: - stretch = stretch_above - editable_pos_idx = [ - i for i, p in enumerate(gp.pos) if p[1] == self._row_idx - ] - editable_pos_idx.sort(key=lambda i: gp.patt[i]) - else: - stretch = stretch_left - editable_pos_idx = [ - i for i, p in enumerate(gp.pos) if p[0] == self._col_idx - ] - editable_pos_idx.sort() + for i in range(self._row_idx + 1, num_row): + row_map[i] = i - 1 + col_map = {i: i for i in range(num_col)} + if not self._fuse_row: + for i in range(self._col_idx + 1, num_col): + col_map[i] = i - 1 + return RowColMap(row_map, col_map) - pos = list(map(stretch, gp.pos)) - if left_points is None or left_points == 0: - yield gp.__class__(gp.patt, pos) - if left_points == 0: - return - row_shift = int(self._fuse_row) - col_shift = 1 - int(self._fuse_row) - for left_points_so_far, i in enumerate(editable_pos_idx): - pos[i] = (pos[i][0] - col_shift, pos[i][1] - row_shift) - if left_points is None or left_points_so_far + 1 == left_points: - yield gp.__class__(gp.patt, pos) - if left_points_so_far + 1 == left_points: - break + def _fuse_gps(self, gps: Iterable["GriddedPerm"]): + return self.upward_closure(self.fuse_map.map_gps(gps)) - def _fuse_counter( - self, gridded_perms: Iterable[GriddedPerm] - ) -> Counter[GriddedPerm]: + @staticmethod + def upward_closure(gps: Iterable[GriddedPerm]) -> List[GriddedPerm]: """ - Count the multiplicities of a set of gridded permutations after the - fusion. - - Return a Counter of gridded permutations with their multiplicities. + Return the upward closure of the gps. + That is, only those which are not contained in any gp but itself. + TODO: make this the upward closure in the actual perm poset. """ - fuse_counter: Counter[GriddedPerm] = collections.Counter() - for gp in gridded_perms: - fused_perm = self.fuse_gridded_perm(gp) - fuse_counter[fused_perm] += 1 - return fuse_counter + return [gp1 for gp1 in gps if all(gp1 not in gp2 for gp2 in gps if gp2 != gp1)] - @property - def obstruction_fuse_counter(self) -> Counter[GriddedPerm]: + def fused_obs_reqs_and_params(self) -> UninitializedTiling: """ - Counter of multiplicities of fused obstructions. - """ - if self._obstruction_fuse_counter is not None: - return self._obstruction_fuse_counter - fuse_counter = self._fuse_counter(self._tiling.obstructions) - self._obstruction_fuse_counter = fuse_counter - return self._obstruction_fuse_counter - - @property - def positive_left_right_requirements( - self, - ) -> Tuple[Tuple[GriddedPerm, ...], Tuple[GriddedPerm, ...]]: - """ - Return the pair of requirements that ensures the left contains at least - one point, and the right contains at least one point. - """ - left, right = [], [] - for (x, y) in self._tiling.active_cells: - if self._fuse_row and y == self._row_idx: - left.append(GriddedPerm.single_cell((0,), (x, y))) - right.append(GriddedPerm.single_cell((0,), (x, y + 1))) - if not self._fuse_row and x == self._col_idx: - left.append(GriddedPerm.single_cell((0,), (x, y))) - right.append(GriddedPerm.single_cell((0,), (x + 1, y))) - return tuple(sorted(left)), tuple(sorted(right)) - - def new_positive_requirement(self) -> List[GriddedPerm]: - cells = [ - (x, y) - for (x, y) in self._tiling.active_cells - if (self._fuse_row and y == self._row_idx) - or (not self._fuse_row and x == self._col_idx) - ] - if self._positive_left and self._positive_right: - cells.sort() - res = [] - for idx, c1 in enumerate(cells): - for c2 in cells[idx:]: - res.append(GriddedPerm((0, 1), (c1, c2))) - if self._fuse_row: - res.append(GriddedPerm((1, 0), (c1, c2))) - else: - res.append(GriddedPerm((1, 0), (c2, c1))) - return sorted(res) - if self._positive_left or self._positive_right: - return sorted(GriddedPerm.single_cell((0,), cell) for cell in cells) - raise ValueError("no positive left right requirement") - - def is_positive_left_or_right_requirement( - self, requirement: Tuple[GriddedPerm, ...] - ) -> bool: - """ - Return True if the requirement is a positive right or left requirement, - but also set this to True on the algorithm, as these will be skipped - when determining whether or not fusable. - """ - left, right = self.positive_left_right_requirements - if requirement == left: - self._positive_left = True - return True - if requirement == right: - self._positive_right = True - return True - return False - - def min_left_right_points(self) -> Tuple[int, int]: - # Make sure that the req fuse counter has been computed so that - # positive left and right or fine - # pylint: disable=pointless-statement - self.requirements_fuse_counters - return int(self._positive_left), int(self._positive_right) - - @property - def requirements_fuse_counters(self) -> List[Counter[GriddedPerm]]: - """ - List of fuse counters for each of the requirements list of the tiling. - """ - if self._requirements_fuse_counters is not None: - return self._requirements_fuse_counters - counters = [ - self._fuse_counter(req_list) - for req_list in self._tiling.requirements - if not self.is_positive_left_or_right_requirement(req_list) - ] - self._requirements_fuse_counters = counters - return self._requirements_fuse_counters - - @property - def assumptions_fuse_counters(self) -> List[Counter[GriddedPerm]]: - """ - List of fuse counters for each of the TrackedAssumptions of the tiling. - """ - if self._assumptions_fuse_counters is not None: - return self._assumptions_fuse_counters - counters = [ - self._fuse_counter(assumption.gps) - for assumption in self._tiling.assumptions - ] - self._assumptions_fuse_counters = counters - return self._assumptions_fuse_counters + Return the fused obs, reqs, and params.""" + return ( + tuple(self._fuse_gps(self.tiling.obstructions)), + tuple(tuple(self._fuse_gps(req)) for req in self.tiling.requirements), + tuple(self.fused_param(param) for param in self.tiling.parameters), + ) - def _can_fuse_set_of_gridded_perms( - self, fuse_counter: Counter[GriddedPerm] - ) -> bool: - """ - Check if a set of gridded permutations can be fused. - """ + def is_fusable_param(self, parameter_counter: ParameterCounter): return all( - self._is_valid_count(count, gp) for gp, count in fuse_counter.items() + self.is_fusable_preimage(preimage) + for preimage in parameter_counter.counters ) - def _is_valid_count(self, count: int, gp: GriddedPerm) -> bool: - """ - Check if the fuse count `count` for a given gridded permutation `gp` is - valid. - """ - return self._point_in_fuse_region(gp) + 1 == count - - def _point_in_fuse_region(self, fused_gp: GriddedPerm) -> int: - """ - Return the number of point of the gridded permutation `fused_gp` in the - fused row or column. - """ + def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: + row1, row2 = self.get_preimage_fuse_indices(preimage) + if row1 is not None and row2 is not None and row1 + 1 != row2: + return False if self._fuse_row: - return sum(1 for cell in fused_gp.pos if cell[1] == self._row_idx) - return sum(1 for cell in fused_gp.pos if cell[0] == self._col_idx) - - def _can_fuse_assumption( - self, assumption: TrackingAssumption, fuse_counter: Counter[GriddedPerm] - ) -> bool: - """ - Return True if an assumption can be fused. That is, prefusion, the gps - are all contained entirely on the left of the fusion region, entirely - on the right, or split in every possible way. - """ - return self._can_fuse_set_of_gridded_perms(fuse_counter) or ( - all(count == 1 for gp, count in fuse_counter.items()) - and self._is_one_sided_assumption(assumption) - ) + fuse_region = self.tiling.cells_in_row(self._row_idx).union( + self.tiling.cells_in_row(self._row_idx + 1) + ) + else: + fuse_region = self.tiling.cells_in_row(self._col_idx).union( + self.tiling.cells_in_col(self._col_idx + 1) + ) + if preimage.active_region(self.tiling).intersection(fuse_region): + return self.fused_preimage(preimage) == self.new_parameter().counters[0] + return True - def _is_one_sided_assumption(self, assumption: TrackingAssumption) -> bool: + def get_preimage_fuse_indices( + self, preimage: PreimageCounter + ) -> Tuple[Optional[int], Optional[int]]: """ - Return True if all of the assumption is contained either entirely on - the left, or entirely on the right. + Return the max of the preimage of self._row_idx, and min of the preimage + of self._row_idx + 1. If either is None, it means this column is empty + on the preimage tiling. """ if self._fuse_row: - return all( - y != self._row_idx for gp in assumption.gps for _, y in gp.pos - ) or all(y != self._row_idx + 1 for gp in assumption.gps for _, y in gp.pos) - return all( - x != self._col_idx for gp in assumption.gps for x, _ in gp.pos - ) or all(x != self._col_idx + 1 for gp in assumption.gps for x, _ in gp.pos) - - def is_left_sided_assumption(self, assumption: TrackingAssumption) -> bool: - if self._fuse_row: - return all( - y != self._row_idx + 1 for gp in assumption.gps for _, y in gp.pos + row1 = max(preimage.map.preimage_row(self._row_idx), default=None) + row2 = min(preimage.map.preimage_row(self._row_idx + 1), default=None) + else: + row1 = max(preimage.map.preimage_col(self._col_idx), default=None) + row2 = min(preimage.map.preimage_col(self._col_idx + 1), default=None) + return row1, row2 + + def fused_preimage(self, preimage: PreimageCounter) -> PreimageCounter: + """Return the fused preimage.""" + row_idx, col_idx = None, None + row1, row2 = self.get_preimage_fuse_indices(preimage) + if row1 is None or row2 is None: + return PreimageCounter( + self.tiling.__class__( + preimage.tiling.obstructions, preimage.tiling.requirements + ), + preimage.map.compose(self.fuse_map), ) - return all(x != self._col_idx + 1 for gp in assumption.gps for x, _ in gp.pos) - - def is_right_sided_assumption(self, assumption: TrackingAssumption) -> bool: if self._fuse_row: - return all(y != self._row_idx for gp in assumption.gps for _, y in gp.pos) - return all(x != self._col_idx for gp in assumption.gps for x, _ in gp.pos) - - def new_assumption(self) -> TrackingAssumption: - """ - Return the assumption that needs to counted in order to enumerate. - """ - return TrackingAssumption( - GriddedPerm.single_cell((0,), cell) - for cell in self._tiling.active_cells - if (self._fuse_row and cell[1] == self._row_idx) - or (not self._fuse_row and cell[0] == self._col_idx) + row_idx = row1 + else: + col_idx = row1 + fuse_algo = Fusion(preimage.tiling, row_idx, col_idx, False) + fused_tiling = fuse_algo.fused_tiling() + fused_map = RowColMap( + { + fuse_algo.fuse_map.map_row(a): self.fuse_map.map_row(b) + for a, b in preimage.map.row_map.items() + }, + { + fuse_algo.fuse_map.map_col(a): self.fuse_map.map_col(b) + for a, b in preimage.map.col_map.items() + }, ) + return PreimageCounter(fused_tiling, fused_map) - def _num_fusing_assumptions(self) -> int: - if self._fuse_row: - fusing_cells = [ - (i, self._row_idx) for i in range(self._tiling.dimensions[0]) - ] + [(i, self._row_idx + 1) for i in range(self._tiling.dimensions[0])] - else: - fusing_cells = [ - (self._col_idx, i) for i in range(self._tiling.dimensions[1]) - ] + [(self._col_idx + 1, i) for i in range(self._tiling.dimensions[1])] - return len( - [ - assumption - for assumption in self._tiling.assumptions - if all(cell in fusing_cells for gp in assumption.gps for cell in gp.pos) - ] + def fused_param(self, parameter: ParameterCounter) -> ParameterCounter: + return ParameterCounter( + [self.fused_preimage(preimage) for preimage in parameter.counters] ) - def _check_isolation_level(self) -> bool: + def unfused_fused_obs_reqs_and_params(self) -> UninitializedTiling: """ - Checks whether the requirements for self.isolation_level are met. + Return the tiling that is created by fusing and then unfusing the tiling. """ - if self.isolation_level is None: - return True - - if self.isolation_level == "noninteracting": - return self._num_fusing_assumptions() <= 1 - - if self.isolation_level == "isolated": - return len(self._tiling.assumptions) == 0 or ( - len(self._tiling.assumptions) == 1 - and self._num_fusing_assumptions() == 1 - ) - - raise RuntimeError(f"{self.isolation_level} is an invalid isolation_level") + obs, reqs, _ = self.fused_obs_reqs_and_params() + return ( + tuple(self.fuse_map.preimage_gps(obs)), + tuple(tuple(self.fuse_map.preimage_gps(req)) for req in reqs), + tuple(), + ) - def fusable(self) -> bool: + def fusable(self): """ - Check if the fusion is possible. + Return True if tiling is fusable. """ - obs_fusable = self._can_fuse_set_of_gridded_perms(self.obstruction_fuse_counter) - req_fusable = all( - self._can_fuse_set_of_gridded_perms(counter) - for counter in self.requirements_fuse_counters - ) - ass_fusable = all( - self._can_fuse_assumption(assumption, counter) - for assumption, counter in zip( - self._tiling.assumptions, self.assumptions_fuse_counters - ) - ) - return ( - obs_fusable - and req_fusable - and ass_fusable - and self._check_isolation_level() - ) + if any( + not self.is_fusable_param(parameter) for parameter in self.tiling.parameters + ): + return False + obs, reqs, _ = self.unfused_fused_obs_reqs_and_params() + return self.tiling == self.tiling.add_obstructions_and_requirements(obs, reqs) def fused_tiling(self) -> "Tiling": """ - Return the fused tiling. + Return the fused tiling after applying the fuse map. """ - if self._fused_tiling is None: - assumptions = [ - ass.__class__(gps) - for ass, gps in zip( - self._tiling.assumptions, self.assumptions_fuse_counters - ) - ] - if self._tracked: - assumptions.append(self.new_assumption()) - requirements = list(list(fc) for fc in self.requirements_fuse_counters) - if self._positive_left or self._positive_right: - new_positive_requirement = self.new_positive_requirement() - requirements = requirements + [new_positive_requirement] - self._fused_tiling = self._tiling.__class__( - obstructions=self.obstruction_fuse_counter.keys(), - requirements=requirements, - assumptions=assumptions, - ) - return self._fused_tiling + obs, reqs, params = self.fused_obs_reqs_and_params() + if self.tracked: + params += (self.new_parameter(),) + return self.tiling.__class__(obs, reqs, params) + + def new_parameter(self): + """ + Return the parameter needed in order to count the fusion. + """ + return ParameterCounter( + [PreimageCounter(self.tiling.remove_parameters(), self.fuse_map)] + ) diff --git a/tilings/algorithms/general_fusion.py b/tilings/algorithms/general_fusion.py deleted file mode 100644 index cea092dd..00000000 --- a/tilings/algorithms/general_fusion.py +++ /dev/null @@ -1,392 +0,0 @@ -# pylint: skip-file -from itertools import chain -from typing import Counter, Dict, Iterator, List, Optional - -from tilings.algorithms.fusion import Fusion -from tilings.assumptions import TrackingAssumption -from tilings.griddedperm import GriddedPerm -from tilings.tiling import Tiling - - -class GeneralFusion(Fusion): - def __init__(self, *args, **kwargs): - self._extra_obs: Optional[List[GriddedPerm]] = None - self._extra_reqs: Optional[List[List[GriddedPerm]]] = None - super().__init__(*args, **kwargs) - - def is_crossing(self, gp: GriddedPerm) -> bool: - """ - Return True if the gridded permutation `gp` is - crossing between the fused rows or cols. - """ - return bool( - self._fuse_row - and any(y == self._row_idx for _, y in gp.pos) - and any(y == self._row_idx for _, y in gp.pos) - ) or bool( - not self._fuse_row - and any(x == self._col_idx for x, _ in gp.pos) - and any(x == self._col_idx for x, _ in gp.pos) - ) - - @property - def obstruction_fuse_counter(self) -> Counter[GriddedPerm]: - """ - Counter of multiplicities of fused obstructions. - - Crossing obstructions between first cell and second cell - are ignored. - """ - if self._obstruction_fuse_counter is not None: - return self._obstruction_fuse_counter - obs = (ob for ob in self._tiling.obstructions if not self.is_crossing(ob)) - fuse_counter = self._fuse_counter(obs) - self._obstruction_fuse_counter = fuse_counter - return self._obstruction_fuse_counter - - @property - def requirements_fuse_counters(self) -> List[Counter[GriddedPerm]]: - """ - List of fuse counters for each of the requirements list of the tiling. - """ - if self._requirements_fuse_counters is not None: - return self._requirements_fuse_counters - counters = [ - self._fuse_counter(req_list) - for req_list in self._tiling.requirements - # if not self.is_positive_left_or_right_requirement(req_list) - # TODO: don't check for positive cells - ] - self._requirements_fuse_counters = counters - return self._requirements_fuse_counters - - def obstructions_to_add(self) -> Iterator[GriddedPerm]: - """ - Iterator over all the obstructions obtained by fusing obstructions of - the tiling and then unfusing it in all possible ways. Crossing - obstructions between first cell and second cell are not processed. - """ - return chain.from_iterable( - self.unfuse_gridded_perm(ob) for ob in self.obstruction_fuse_counter - ) - - def requirements_to_add(self) -> Iterator[Iterator[GriddedPerm]]: - for req in self.requirements_fuse_counters: - yield chain.from_iterable(self.unfuse_gridded_perm(gp) for gp in req) - - def _can_fuse_set_of_gridded_perms( - self, fuse_counter: Counter[GriddedPerm] - ) -> bool: - raise NotImplementedError - - def _is_valid_count(self, count: int, gp: GriddedPerm) -> bool: - raise NotImplementedError - - def fusable(self) -> bool: - split_obs = tuple(self.obstructions_to_add()) - split_reqs = tuple(tuple(req) for req in self.requirements_to_add()) - new_tiling = self._tiling.add_obstructions_and_requirements( - split_obs, split_reqs, remove_empty_rows_and_cols=False - ) - self._extra_obs = [ - gp for gp in self._tiling.obstructions if gp not in split_obs - ] - extra_reqs = [ - [gp for gp in split_req if gp in req] - for req, split_req in zip(self._tiling.requirements, split_reqs) - ] - self._extra_reqs = [req for req in extra_reqs if req] - return ( - self._tiling == new_tiling - and not extra_reqs - and all(len(gp) <= 2 for gp in self._extra_obs) - # and len(self._extra_obs) <= 1 - and self._check_isolation_level() - and not any( - ass.is_active_in_cells(self.active_cells()) - and ass.fuse( - self._row_idx if self._fuse_row else self._col_idx, self._fuse_row - ) - != self.new_assumption() - for ass in self._tiling.assumptions - ) - # and self.new_assumption().tiling.dimensions - # in ((2, 1), (1, 2)) # TODO: this is perhaps too restrictive? - ) - - def active_cells(self): - if self._fuse_row: - return self._tiling.cells_in_row(self._row_idx).union( - self._tiling.cells_in_row(self._row_idx + 1) - ) - else: - return self._tiling.cells_in_col(self._col_idx).union( - self._tiling.cells_in_col(self._col_idx + 1) - ) - - def new_assumption(self) -> TrackingAssumption: - """ - Return the assumption that needs to be counted in order to enumerate. - """ - # if not self.fusable(): - # raise ValueError("Tiling is not fusable") - if self._extra_obs is None: - assert self.fusable() - assert self._extra_obs is not None - assert self._extra_reqs is not None - tiling = self._tiling - col_map: Dict[int, int] = dict() - row_map: Dict[int, int] = dict() - for x in range(self._tiling.dimensions[0]): - if not self._fuse_row and x > self._col_idx: - col_map[x] = x - 1 - if not self._fuse_row and x <= self._col_idx: - col_map[x] = x - if self._fuse_row: - col_map[x] = x - for y in range(self._tiling.dimensions[1]): - if self._fuse_row and y > self._row_idx: - row_map[y] = y - 1 - if self._fuse_row and y <= self._row_idx: - row_map[y] = y - if not self._fuse_row: - row_map[y] = y - return TrackingAssumption(tiling, col_map, row_map) - - -def test_ass_map(tiling, original_tiling, verbose=False): - if verbose: - print("=" * 10) - print("TESTING ASS MAP") - print(tiling) - print(original_tiling) - for i in range(6): # these counts should match! - terms = tiling.get_terms(i) - actual = len(list(original_tiling.remove_assumptions().objects_of_size(i))) - computed = sum(k[0] * v for k, v in terms.items()) - assert actual == computed, (i, actual, computed, terms, tiling, original_tiling) - - -if __name__ == "__main__": - - tiling = Tiling.from_dict( - { - "class_module": "tilings.tiling", - "comb_class": "Tiling", - "obstructions": [ - {"patt": [0, 1], "pos": [[0, 0], [1, 0]]}, - {"patt": [0, 1], "pos": [[0, 0], [3, 0]]}, - {"patt": [0, 1], "pos": [[1, 0], [3, 0]]}, - {"patt": [0, 1], "pos": [[2, 0], [3, 0]]}, - {"patt": [0, 2, 1], "pos": [[0, 0], [0, 0], [0, 0]]}, - {"patt": [0, 2, 1], "pos": [[0, 0], [0, 0], [2, 0]]}, - {"patt": [0, 2, 1], "pos": [[0, 0], [2, 0], [2, 0]]}, - {"patt": [0, 2, 1], "pos": [[1, 0], [1, 0], [1, 0]]}, - {"patt": [0, 2, 1], "pos": [[1, 0], [1, 0], [2, 0]]}, - {"patt": [0, 2, 1], "pos": [[1, 0], [2, 0], [2, 0]]}, - {"patt": [0, 2, 1], "pos": [[2, 0], [2, 0], [2, 0]]}, - {"patt": [0, 2, 1], "pos": [[3, 0], [3, 0], [3, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[0, 0], [0, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[0, 0], [2, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[0, 0], [4, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[1, 0], [1, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[1, 0], [2, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[1, 0], [4, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[2, 0], [2, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[2, 0], [4, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[3, 0], [3, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[3, 0], [4, 0], [4, 0], [4, 0]]}, - {"patt": [0, 2, 1, 3], "pos": [[4, 0], [4, 0], [4, 0], [4, 0]]}, - ], - "requirements": [], - "assumptions": [], - } - ) - print(tiling) - gf = GeneralFusion(tiling, col_idx=0, tracked=True) - print(gf.fusable()) - print(gf.fused_tiling()) - test_ass_map(gf.fused_tiling(), tiling) - - fusable = Tiling( - [ - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (2, 0))), - GriddedPerm((0, 1), ((1, 0), (1, 0))), - GriddedPerm((0, 1), ((1, 0), (2, 0))), - GriddedPerm((0, 1), ((2, 0), (2, 0))), - ] - ) - - gf = GeneralFusion(tiling=fusable, col_idx=1, tracked=True) - - print(gf.fusable()) - print(gf.fused_tiling()) - - test_ass_map(gf.fused_tiling(), fusable) - - for i in range(5): - terms = gf.fused_tiling().get_terms(i) - print(i, terms) - print("actual:", len(list(gf._tiling.objects_of_size(i)))) - print("computed:", sum(k[0] * v for k, v in terms.items())) - - comp_fusable = Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 0), (0, 1))), - GriddedPerm((0, 1, 2), ((0, 0), (0, 2), (0, 2))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 2), (0, 2))), - GriddedPerm((0, 2, 1), ((0, 0), (0, 2), (0, 0))), - GriddedPerm((0, 2, 1), ((0, 1), (0, 2), (0, 1))), - GriddedPerm((0, 2, 3, 1), ((0, 0), (0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 2, 3, 1), ((0, 2), (0, 2), (0, 2), (0, 2))), - GriddedPerm((0, 3, 1, 2), ((0, 0), (0, 0), (0, 0), (0, 0))), - GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 3, 1, 2), ((0, 2), (0, 2), (0, 2), (0, 2))), - ), - requirements=(), - assumptions=(), - ) # 3 without assumption - - gf = GeneralFusion(comp_fusable, row_idx=0, tracked=True) - fused_tiling = gf.fused_tiling() # 2 with assumption - - test_ass_map(fused_tiling, comp_fusable) - - ass = fused_tiling.assumptions[0] - print("===== component fusable tiling =====") - print(comp_fusable) - print("==== end ====") - - print("===== the fused tiling =====") - print(fused_tiling) - print("==== end ====") - for gp in sorted(fused_tiling.objects_of_size(3)): - print(gp) - for gp2 in sorted( - fused_tiling.assumptions[0].gridding_counter._griddings(3)[gp] - ): - print("\t", gp2) - assert 0 - - for i in range(6): - terms = fused_tiling.get_terms(i) - print(i, terms) - print("actual:", len(list(gf._tiling.objects_of_size(i)))) - print("computed:", sum(k[0] * v for k, v in terms.items())) - - unfused_positive_tiling = comp_fusable.insert_cell((0, 2)) - - positive_fused_tiling = fused_tiling.insert_cell((0, 1)) - - test_ass_map(positive_fused_tiling, unfused_positive_tiling) - - print("===== the positive tiling =====") - print(positive_fused_tiling) # 5 with assumption? - print("==== end ====") - - unfused_placed_tiling = unfused_positive_tiling.place_point_in_cell((0, 2), 0) - - placed_fused_tiling = positive_fused_tiling.place_point_in_cell((0, 1), 0) - - test_ass_map(placed_fused_tiling, unfused_placed_tiling, verbose=False) - - print("===== the placed tiling =====") - print(placed_fused_tiling) # 5.5, i.e. the one in the middle of the eqv path 5 -> 6 - print("==== end ====") - separated_tiling = placed_fused_tiling.row_and_column_separation() - unfused_separated_tiling = Tiling( - obstructions=( - GriddedPerm((0, 1), ((0, 1), (0, 3))), - GriddedPerm((0, 1), ((0, 1), (0, 4))), - GriddedPerm((0, 1), ((0, 1), (2, 2))), - GriddedPerm((0, 1), ((0, 3), (0, 4))), - GriddedPerm((0, 1), ((1, 5), (1, 5))), - GriddedPerm((0, 1), ((2, 0), (2, 2))), - GriddedPerm((1, 0), ((1, 5), (1, 5))), - GriddedPerm((0, 1, 2), ((0, 1), (0, 6), (0, 6))), - GriddedPerm((0, 1, 2), ((0, 3), (0, 6), (0, 6))), - GriddedPerm((0, 1, 2), ((0, 4), (0, 6), (0, 6))), - GriddedPerm((0, 2, 1), ((0, 1), (0, 6), (0, 1))), - GriddedPerm((0, 2, 1), ((0, 3), (0, 6), (0, 3))), - GriddedPerm((0, 2, 1), ((0, 4), (0, 6), (0, 4))), - GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 2, 3, 1), ((0, 3), (0, 3), (0, 3), (0, 3))), - GriddedPerm((0, 2, 3, 1), ((0, 4), (0, 4), (0, 4), (0, 4))), - GriddedPerm((0, 2, 3, 1), ((0, 6), (0, 6), (0, 6), (0, 6))), - GriddedPerm((0, 2, 3, 1), ((2, 0), (2, 0), (2, 0), (2, 0))), - GriddedPerm((0, 2, 3, 1), ((2, 2), (2, 2), (2, 2), (2, 2))), - GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (0, 1))), - GriddedPerm((0, 3, 1, 2), ((0, 3), (0, 3), (0, 3), (0, 3))), - GriddedPerm((0, 3, 1, 2), ((0, 4), (0, 4), (0, 4), (0, 4))), - GriddedPerm((0, 3, 1, 2), ((0, 6), (0, 6), (0, 6), (0, 6))), - GriddedPerm((0, 3, 1, 2), ((2, 0), (2, 0), (2, 0), (2, 0))), - GriddedPerm((0, 3, 1, 2), ((2, 2), (2, 2), (2, 2), (2, 2))), - ), - requirements=((GriddedPerm((0,), ((1, 5),)),),), - assumptions=(), - ) # only separating rows, so can't use row_column_separation method - - print(repr(unfused_separated_tiling)) - print(separated_tiling) - test_ass_map(separated_tiling, unfused_separated_tiling, verbose=True) - - # separated_assless_tiling = ( - # placed_fused_tiling.remove_assumptions().row_and_column_separation() - # ) - # print(separated_assless_tiling) - # separated_comp_fusable = Tiling( - # [ - # GriddedPerm((0, 1), ((0, 1), (2, 2))), - # GriddedPerm((0, 1), ((2, 0), (2, 2))), - # GriddedPerm((0, 1), ((0, 1), (0, 3))), - # ], - # remove_empty_rows_and_cols=False, - # ) - # print(separated_comp_fusable) - # separable_map = { - # (0, 1): (0, 1), - # (0, 3): (0, 1), - # (1, 0): (1, 0), - # (2, 2): (2, 0), - # (2, 0): (2, 0), - # } - - # separable_ass = TrackingAssumption(separated_comp_fusable, separable_map) - # unfused_separated_tiling = unfused_placed_tiling.row_and_column_separation() - # separated_tiling = separated_assless_tiling.add_assumption(separable_ass) - print("===== the separated tiling =====") - print(separated_tiling) # 6, i.e. the one in the middle of the eqv path 5 -> 6 - print("==== end ====") - - # TODO: unfused tiling should not separate last column - # test_ass_map(separated_tiling, unfused_separated_tiling) - - for i in range(6): # these counts should match! - print("====placed====") - terms = placed_fused_tiling.get_terms(i) - print(i, terms) - print( - "actual:", - len(list(unfused_placed_tiling.objects_of_size(i))), - ) - print("computed:", sum(k[0] * v for k, v in terms.items())) - print("====positive====") - terms = positive_fused_tiling.get_terms(i) - print(i, terms) - print( - "actual:", - len(list(unfused_positive_tiling.objects_of_size(i))), - ) - print("computed:", sum(k[0] * v for k, v in terms.items())) - print("====separated====") - terms = separated_tiling.get_terms(i) - print(i, terms) - print( - "actual:", - len(list(unfused_separated_tiling.objects_of_size(i))), - ) - print("computed:", sum(k[0] * v for k, v in terms.items())) - print() diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index c59f05db..6f5b7fee 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -28,6 +28,10 @@ def _init_checked(self): """ assert not self.tiling.parameters + def is_empty(self): + """Return true if tiling is empty, and therefore always counts 0.""" + return self.tiling.is_empty() + def remove_empty_rows_and_cols(self) -> None: """ Update the col and row maps after removing cols and rows that @@ -159,7 +163,9 @@ class ParameterCounter: """ def __init__(self, counters: Iterable[PreimageCounter]): - self.counters = tuple(sorted(counters)) + self.counters = tuple( + sorted(itertools.filterfalse(PreimageCounter.is_empty, counters)) + ) def active_regions(self, tiling: "Tiling") -> Iterator[Set[Cell]]: """ @@ -175,7 +181,9 @@ def sub_param( ) -> "ParameterCounter": res = [] for preimage in self.counters: - if preimage.active_region(underlying_tiling) <= cells: + active_region = preimage.active_region(underlying_tiling) + mapped_active_region = set(map(preimage.map.map_cell, active_region)) + if mapped_active_region <= cells: res.append(preimage.sub_preimage(cells)) return ParameterCounter(res) diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py new file mode 100644 index 00000000..39441ab1 --- /dev/null +++ b/tilings/strategies/parameter_strategies.py @@ -0,0 +1,76 @@ +from typing import Iterator, List, Optional, Tuple + +from comb_spec_searcher.exception import StrategyDoesNotApply +from comb_spec_searcher.strategies import Constructor, Strategy +from tilings import GriddedPerm, Tiling +from tilings.parameter_counter import ParameterCounter, PreimageCounter + + +class RemoveIdentityPreimage(Strategy[Tiling, GriddedPerm]): + def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: + applied = False + params: List[List[PreimageCounter]] = [] + for param in comb_class.parameters: + params.append([]) + for preimg in param.counters: + if ( + preimg.map.is_identity() + and preimg.tiling == comb_class.remove_parameters() + ): + applied = True + else: + params[-1].append(preimg) + if not applied: + raise StrategyDoesNotApply + t = comb_class.remove_parameters().add_parameters(map(ParameterCounter, params)) + return (t,) + + def constructor( + self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None + ) -> Constructor: + raise NotImplementedError + + def reverse_constructor( + self, + idx: int, + comb_class: Tiling, + children: Optional[Tuple[Tiling, ...]] = None, + ) -> Constructor: + raise NotImplementedError + + def backward_map( + self, + comb_class: Tiling, + objs: Tuple[Optional[GriddedPerm], ...], + children: Optional[Tuple[Tiling, ...]] = None, + ) -> Iterator[GriddedPerm]: + raise NotImplementedError + + def forward_map( + self, + comb_class: Tiling, + obj: GriddedPerm, + children: Optional[Tuple[Tiling, ...]] = None, + ) -> Tuple[Optional[GriddedPerm], ...]: + raise NotImplementedError + + def can_be_equivalent(self) -> bool: + return False + + def formal_step(self) -> str: + return "remove identity preimages" + + @classmethod + def from_dict(cls, d: dict) -> "RemoveIdentityPreimage": + raise NotImplementedError + + def is_reversible(self, comb_class: Tiling) -> bool: + return True + + def is_two_way(self, comb_class: Tiling) -> bool: + return True + + def shifts( + self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None + ) -> Tuple[int]: + return (0,) From 791123c2e85a68582f1f8d5d8ba9ba2b85a926f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 11 Nov 2021 17:14:04 +0000 Subject: [PATCH 20/41] fix a bug with active region of preimages (#383) --- tilings/parameter_counter.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 6f5b7fee..ffcf5c48 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -61,6 +61,8 @@ def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": def active_region(self, tiling: "Tiling") -> Set[Cell]: """ Yield the active region of the preimage counter. + + The cells are on the underlying tiling. """ res = set() for cell in self.tiling.active_cells: @@ -69,7 +71,7 @@ def active_region(self, tiling: "Tiling") -> Set[Cell]: extra_obs, extra_reqs = self.extra_obs_and_reqs(tiling) for gp in itertools.chain(extra_obs, *extra_reqs): res.update(gp.pos) - return res + return set(self.map.map_cell(cell) for cell in res) def sub_preimage(self, cells: Set[Cell]) -> "PreimageCounter": precells = set( @@ -170,11 +172,11 @@ def __init__(self, counters: Iterable[PreimageCounter]): def active_regions(self, tiling: "Tiling") -> Iterator[Set[Cell]]: """ Yield the active regions of the preimage counters. + + The cell are on the underlying tiling. """ for preimage in self: - yield set( - preimage.map.map_cell(cell) for cell in preimage.active_region(tiling) - ) + yield preimage.active_region(tiling) def sub_param( self, cells: Set[Cell], underlying_tiling: "Tiling" From 7de3179c874f9ea62d78881019e696aa5c72122c Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 11 Nov 2021 18:46:00 +0000 Subject: [PATCH 21/41] Fixing map primage counter method (#382) * fix map_preimage_counter * careful when copy pasting!!! * one less local variable --- .../strategies/test_row_column_separation.py | 130 ++++++++++++++++++ tilings/map.py | 28 ++-- 2 files changed, 149 insertions(+), 9 deletions(-) diff --git a/tests/strategies/test_row_column_separation.py b/tests/strategies/test_row_column_separation.py index b3c29fc5..a275dc23 100644 --- a/tests/strategies/test_row_column_separation.py +++ b/tests/strategies/test_row_column_separation.py @@ -335,3 +335,133 @@ def test_mapping_parameter(): strategy = RowColumnSeparationStrategy() rule = strategy(tiling) assert strategy.extra_parameters(tiling, rule.children) == ({"k_0": "k_0"},) + + +def test_with_parameters(): + tiling = Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 3), (1, 3))), + GriddedPerm((0, 1), ((3, 4), (3, 4))), + GriddedPerm((1, 0), ((1, 3), (1, 3))), + GriddedPerm((1, 0), ((2, 4), (3, 4))), + GriddedPerm((0, 1, 2), ((0, 1), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((0, 1), (3, 2), (3, 4))), + GriddedPerm((0, 1, 2), ((2, 1), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((2, 1), (3, 2), (3, 4))), + GriddedPerm((0, 1, 2), ((2, 2), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((2, 2), (3, 2), (3, 4))), + GriddedPerm((0, 1, 2), ((3, 0), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((3, 0), (3, 2), (3, 4))), + GriddedPerm((0, 2, 1), ((2, 2), (2, 2), (3, 2))), + GriddedPerm((0, 2, 1), ((2, 2), (2, 4), (3, 2))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 2), (3, 0))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 4), (3, 0))), + GriddedPerm((1, 2, 0), ((2, 4), (2, 4), (2, 4))), + GriddedPerm((2, 0, 1), ((2, 4), (2, 4), (2, 4))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (0, 1), (2, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (0, 1), (2, 1), (2, 1))), + GriddedPerm((0, 2, 3, 1), ((0, 1), (2, 1), (2, 1), (2, 1))), + GriddedPerm((0, 2, 3, 1), ((2, 1), (2, 1), (2, 1), (2, 1))), + GriddedPerm((0, 2, 3, 1), ((2, 2), (2, 2), (2, 2), (2, 2))), + GriddedPerm((0, 2, 3, 1), ((2, 2), (2, 2), (2, 4), (2, 2))), + GriddedPerm((0, 2, 3, 1), ((2, 2), (2, 4), (2, 4), (2, 2))), + GriddedPerm((0, 2, 3, 1), ((3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 3, 1), ((3, 2), (3, 2), (3, 2), (3, 2))), + GriddedPerm((0, 2, 3, 1), ((3, 2), (3, 2), (3, 4), (3, 2))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (0, 1))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (0, 1), (2, 1))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (0, 1), (2, 1), (2, 1))), + GriddedPerm((0, 3, 1, 2), ((0, 1), (2, 1), (2, 1), (2, 1))), + GriddedPerm((0, 3, 1, 2), ((2, 1), (2, 1), (2, 1), (2, 1))), + GriddedPerm((0, 3, 1, 2), ((2, 2), (2, 2), (2, 2), (2, 2))), + GriddedPerm((0, 3, 1, 2), ((2, 2), (2, 4), (2, 2), (2, 2))), + GriddedPerm((0, 3, 1, 2), ((2, 2), (2, 4), (2, 2), (2, 4))), + GriddedPerm((0, 3, 1, 2), ((3, 0), (3, 0), (3, 0), (3, 0))), + GriddedPerm((0, 3, 1, 2), ((3, 2), (3, 2), (3, 2), (3, 2))), + GriddedPerm((0, 3, 1, 2), ((3, 2), (3, 4), (3, 2), (3, 2))), + GriddedPerm((0, 3, 1, 2), ((3, 2), (3, 4), (3, 2), (3, 4))), + ), + requirements=((GriddedPerm((0,), ((1, 3),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 3), (0, 3))), + GriddedPerm((0, 1), ((3, 4), (3, 4))), + GriddedPerm((1, 0), ((0, 3), (0, 3))), + GriddedPerm((1, 0), ((1, 4), (3, 4))), + GriddedPerm((0, 1, 2), ((1, 2), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((1, 2), (3, 2), (3, 4))), + GriddedPerm((0, 1, 2), ((2, 1), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((2, 1), (3, 2), (3, 4))), + GriddedPerm((0, 1, 2), ((3, 0), (3, 2), (3, 2))), + GriddedPerm((0, 1, 2), ((3, 0), (3, 2), (3, 4))), + GriddedPerm((0, 2, 1), ((1, 2), (1, 2), (3, 2))), + GriddedPerm((0, 2, 1), ((1, 2), (1, 4), (3, 2))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 2), (3, 0))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 4), (3, 0))), + GriddedPerm((1, 2, 0), ((1, 4), (1, 4), (1, 4))), + GriddedPerm((2, 0, 1), ((1, 4), (1, 4), (1, 4))), + GriddedPerm( + (0, 2, 3, 1), ((1, 2), (1, 2), (1, 2), (1, 2)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 2), (1, 2), (1, 4), (1, 2)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 2), (1, 4), (1, 4), (1, 2)) + ), + GriddedPerm( + (0, 2, 3, 1), ((2, 1), (2, 1), (2, 1), (2, 1)) + ), + GriddedPerm( + (0, 2, 3, 1), ((3, 0), (3, 0), (3, 0), (3, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((3, 2), (3, 2), (3, 2), (3, 2)) + ), + GriddedPerm( + (0, 2, 3, 1), ((3, 2), (3, 2), (3, 4), (3, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 2), (1, 2), (1, 2), (1, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 2), (1, 4), (1, 2), (1, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 2), (1, 4), (1, 2), (1, 4)) + ), + GriddedPerm( + (0, 3, 1, 2), ((2, 1), (2, 1), (2, 1), (2, 1)) + ), + GriddedPerm( + (0, 3, 1, 2), ((3, 0), (3, 0), (3, 0), (3, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((3, 2), (3, 2), (3, 2), (3, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((3, 2), (3, 4), (3, 2), (3, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((3, 2), (3, 4), (3, 2), (3, 4)) + ), + ), + requirements=((GriddedPerm((0,), ((0, 3),)),),), + parameters=(), + ), + RowColMap( + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}, {0: 1, 1: 2, 2: 2, 3: 3} + ), + ), + ) + ), + ), + ) + rule = RowColumnSeparationStrategy()(tiling) + for i in range(4): + rule.sanity_check(i) diff --git a/tilings/map.py b/tilings/map.py index f43f3182..00735319 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -124,17 +124,29 @@ def map_preimage_counter( NOTE: This works if the map is bijective. Not sure about other cases. """ + cols_added_before: Dict[int, int] = {} + rows_added_before: Dict[int, int] = {} + for cell in self.domain(): + col_pos = self.map_cell(cell)[0] - cell[0] + row_pos = self.map_cell(cell)[1] - cell[1] + cols_added_before[cell[0]] = min( + cols_added_before.get(cell[0], col_pos), col_pos + ) + rows_added_before[cell[1]] = min( + rows_added_before.get(cell[1], row_pos), row_pos + ) cell_pos_in_col, cell_pos_in_row = {}, {} col_split = [0 for _ in range(preimg_counter.tiling.dimensions[0])] row_split = [0 for _ in range(preimg_counter.tiling.dimensions[1])] for cell in self.domain(): + col_pos = self.map_cell(cell)[0] - cell[0] - cols_added_before[cell[0]] + row_pos = self.map_cell(cell)[1] - cell[1] - rows_added_before[cell[1]] for pre_cell in preimg_counter.map.preimage_cell(cell): - col_pos = self.map_cell(cell)[0] - cell[0] - row_pos = self.map_cell(cell)[1] - cell[1] cell_pos_in_col[pre_cell] = col_pos cell_pos_in_row[pre_cell] = row_pos - col_split[cell[0]] = max(col_pos + 1, col_split[cell[0]]) - row_split[cell[1]] = max(row_pos + 1, row_split[cell[1]]) + col_split[pre_cell[0]] = max(col_pos + 1, col_split[pre_cell[0]]) + row_split[pre_cell[1]] = max(row_pos + 1, row_split[pre_cell[1]]) + cell_to_col_map = { k: v + sum(col_split[: k[0]]) for k, v in cell_pos_in_col.items() } @@ -147,14 +159,12 @@ def map_preimage_counter( for cell in preimg_counter.tiling.active_cells } ) - projection_map = ( + return preimg_counter.__class__( + preimg_map.map_tiling(preimg_counter.tiling), preimg_map.inverse() .compose(preimg_counter.map) .compose(self) - .to_row_col_map() - ) - return preimg_counter.__class__( - preimg_map.map_tiling(preimg_counter.tiling), projection_map + .to_row_col_map(), ) def is_mappable_gp(self, gp: "GriddedPerm") -> bool: From 9cd966d3375af70a89417f78e94a508ba66cc242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Mon, 15 Nov 2021 14:44:37 +0000 Subject: [PATCH 22/41] displayed legends include label for preimages (#387) --- tilings/algorithms/display.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tilings/algorithms/display.py b/tilings/algorithms/display.py index 6c4ded36..7b5680da 100644 --- a/tilings/algorithms/display.py +++ b/tilings/algorithms/display.py @@ -56,10 +56,11 @@ def __init__(self, tiling: "Tiling"): def ascii(self) -> str: lines = self.grid_lines(self.tiling) + param_lines = self.params_lines() lines.extend(self.legend()) lines.extend(self.crossing_obs_lines(self.tiling.obstructions)) lines.extend(self.req_lines(self.tiling.requirements)) - lines.extend(self.params_lines()) + lines.extend(param_lines) return "\n".join(lines) def html(self) -> str: From 2b166d132dccf379874aaf0af8ac4a65631b8a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Tue, 16 Nov 2021 12:22:33 +0000 Subject: [PATCH 23/41] Getting the component tree (#384) * Implementing the strategies to do req insertion on preimages * using disjoint union strategies on parameters * adding parameter strategies to tracked packs, added a verification stra for params * fix a bug in mapping the sub param to the right factor * restrict the fusion to something reasonable * ignore_parent for disjoint strategy on param * fix mypy and friends * better str for param strategy * ignore parent on remove identity preimage * simplify and fix the json * on/off tests Co-authored-by: Christian Bean --- tests/strategies/test_sanity_check.py | 125 ++++++++++++++ tests/test_bijections.py | 5 +- tests/test_tilescope.py | 1 - tilings/algorithms/fusion.py | 26 ++- tilings/parameter_counter.py | 3 +- tilings/strategies/parameter_strategies.py | 187 ++++++++++++++++++++- tilings/strategy_pack.py | 39 +++-- 7 files changed, 360 insertions(+), 26 deletions(-) diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index d48c6ad2..c9d3aae0 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -1138,6 +1138,131 @@ ), ) ), + FactorStrategy( + partition=(((0, 1),), ((1, 0), (1, 2))), ignore_parent=True, workable=True + )( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 2, 0), ((1, 2), (1, 2), (1, 2))), + GriddedPerm((2, 0, 1), ((1, 2), (1, 2), (1, 2))), + GriddedPerm((0, 2, 3, 1), ((1, 0), (1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 3, 1), ((1, 0), (1, 0), (1, 2), (1, 0))), + GriddedPerm((0, 2, 3, 1), ((1, 0), (1, 2), (1, 2), (1, 0))), + GriddedPerm((0, 3, 1, 2), ((1, 0), (1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 3, 1, 2), ((1, 0), (1, 2), (1, 0), (1, 0))), + GriddedPerm((0, 3, 1, 2), ((1, 0), (1, 2), (1, 0), (1, 2))), + ), + requirements=((GriddedPerm((0,), ((0, 1),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((0, 1), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((0, 2), (0, 2))), + GriddedPerm((1, 0), ((1, 3), (1, 1))), + GriddedPerm((1, 2, 0), ((1, 3), (1, 3), (1, 3))), + GriddedPerm((2, 0, 1), ((1, 3), (1, 3), (1, 3))), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 1), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 1), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 3), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 0), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 1), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 1), (1, 0), (1, 1)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 3), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 3), (1, 0), (1, 3)) + ), + ), + requirements=((GriddedPerm((0,), ((0, 2),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1, 3: 2}, {0: 0, 1: 1}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((1, 0), ((0, 1), (0, 1))), + GriddedPerm((1, 2, 0), ((1, 2), (1, 2), (1, 2))), + GriddedPerm((1, 2, 0), ((1, 2), (1, 3), (1, 2))), + GriddedPerm((1, 2, 0), ((1, 3), (1, 3), (1, 2))), + GriddedPerm((1, 2, 0), ((1, 3), (1, 3), (1, 3))), + GriddedPerm((2, 0, 1), ((1, 2), (1, 2), (1, 2))), + GriddedPerm((2, 0, 1), ((1, 3), (1, 2), (1, 2))), + GriddedPerm((2, 0, 1), ((1, 3), (1, 2), (1, 3))), + GriddedPerm((2, 0, 1), ((1, 3), (1, 3), (1, 3))), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 2), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 0), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 2), (1, 2), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 2), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 2, 3, 1), ((1, 0), (1, 3), (1, 3), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 0), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 2), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 2), (1, 0), (1, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 3), (1, 0), (1, 0)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 3), (1, 0), (1, 2)) + ), + GriddedPerm( + (0, 3, 1, 2), ((1, 0), (1, 3), (1, 0), (1, 3)) + ), + ), + requirements=((GriddedPerm((0,), ((0, 1),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 2}, {0: 0, 1: 1}), + ), + ) + ), + ), + ) + ), ] equiv_rule_to_check = [r for r in rules_to_check if r.is_equivalence()] diff --git a/tests/test_bijections.py b/tests/test_bijections.py index 7cdefff0..567da555 100644 --- a/tests/test_bijections.py +++ b/tests/test_bijections.py @@ -93,6 +93,7 @@ def test_bijection_1(): ) +@pytest.mark.skip def test_bijection_2(): _tester( "0132_0213_0231_0312_0321_1032_1320_2301_3021_3120", @@ -229,6 +230,7 @@ def test_bijection_9_cross_domain(): _bijection_asserter(find_bijection_between(searcher2, searcher1)) +@pytest.mark.skip def test_bijection_10(): pack1 = TileScopePack.requirement_placements() pack1 = pack1.add_verification(BasicVerificationStrategy(), replace=True) @@ -239,6 +241,7 @@ def test_bijection_10(): _bijection_asserter(find_bijection_between(searcher1, searcher2)) +@pytest.mark.skip @pytest.mark.slow def test_bijection_11(): pairs = [ @@ -487,7 +490,7 @@ def test_bijection_14_json(): _bijection_asserter(Bijection.from_dict(json.loads(json.dumps(bi.to_jsonable())))) -@pytest.mark.xfail +@pytest.mark.skip @pytest.mark.slow def test_bijection_15_fusion(): pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index d9d35cd0..34a1fdb3 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -104,7 +104,6 @@ def test_132_321_genf(): ] -@pytest.mark.xfail @pytest.mark.timeout(20) def test_123(): searcher = TileScope((Perm((0, 1, 2)),), point_placements_fusion) diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 72ab2ad9..8021e9c7 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -20,6 +20,10 @@ class Fusion: + MAX_NUMBER_EXTRA = 1 + MAX_LENGTH_EXTRA = 2 + MAX_NUM_PARAMS = 1 + def __init__( self, tiling: "Tiling", @@ -129,7 +133,9 @@ def fused_preimage(self, preimage: PreimageCounter) -> PreimageCounter: row_idx = row1 else: col_idx = row1 - fuse_algo = Fusion(preimage.tiling, row_idx, col_idx, False) + fuse_algo = Fusion( + preimage.tiling.remove_requirements(), row_idx, col_idx, False + ) fused_tiling = fuse_algo.fused_tiling() fused_map = RowColMap( { @@ -168,7 +174,17 @@ def fusable(self): ): return False obs, reqs, _ = self.unfused_fused_obs_reqs_and_params() - return self.tiling == self.tiling.add_obstructions_and_requirements(obs, reqs) + if self.tiling == self.tiling.add_obstructions_and_requirements(obs, reqs): + ft = self.fused_tiling() + if len(ft.parameters) <= self.MAX_NUM_PARAMS: + eobs, ereqs = self.extra_obs_and_reqs() + if ( + not ereqs + and len(eobs) <= self.MAX_NUMBER_EXTRA + and all(len(gp) <= self.MAX_LENGTH_EXTRA for gp in eobs) + ): + return True + return False def fused_tiling(self) -> "Tiling": """ @@ -186,3 +202,9 @@ def new_parameter(self): return ParameterCounter( [PreimageCounter(self.tiling.remove_parameters(), self.fuse_map)] ) + + def extra_obs_and_reqs( + self, + ) -> Tuple[List[GriddedPerm], List[Tuple[GriddedPerm, ...]]]: + ft = self.fused_tiling() + return self.new_parameter().counters[0].extra_obs_and_reqs(ft) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index ffcf5c48..9e9c233a 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -184,8 +184,7 @@ def sub_param( res = [] for preimage in self.counters: active_region = preimage.active_region(underlying_tiling) - mapped_active_region = set(map(preimage.map.map_cell, active_region)) - if mapped_active_region <= cells: + if active_region <= cells: res.append(preimage.sub_preimage(cells)) return ParameterCounter(res) diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 39441ab1..44704333 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -1,12 +1,27 @@ from typing import Iterator, List, Optional, Tuple +from comb_spec_searcher import CombinatorialSpecificationSearcher from comb_spec_searcher.exception import StrategyDoesNotApply -from comb_spec_searcher.strategies import Constructor, Strategy +from comb_spec_searcher.strategies import ( + AbstractStrategy, + Constructor, + DisjointUnion, + DisjointUnionStrategy, + Rule, + Strategy, + StrategyFactory, + StrategyPack, + VerificationStrategy, +) +from comb_spec_searcher.typing import CSSstrategy from tilings import GriddedPerm, Tiling from tilings.parameter_counter import ParameterCounter, PreimageCounter -class RemoveIdentityPreimage(Strategy[Tiling, GriddedPerm]): +class RemoveIdentityPreimageStrategy(Strategy[Tiling, GriddedPerm]): + def __init__(self) -> None: + super().__init__(ignore_parent=True) + def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: applied = False params: List[List[PreimageCounter]] = [] @@ -61,8 +76,16 @@ def formal_step(self) -> str: return "remove identity preimages" @classmethod - def from_dict(cls, d: dict) -> "RemoveIdentityPreimage": - raise NotImplementedError + def from_dict(cls, d: dict) -> "RemoveIdentityPreimageStrategy": + return cls() + + def to_jsonable(self) -> dict: + d = super().to_jsonable() + d.pop("ignore_parent") + d.pop("inferrable") + d.pop("possibly_empty") + d.pop("workable") + return d def is_reversible(self, comb_class: Tiling) -> bool: return True @@ -74,3 +97,159 @@ def shifts( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None ) -> Tuple[int]: return (0,) + + +class DisjointParameterStrategy(DisjointUnionStrategy[Tiling, GriddedPerm]): + def __init__( + self, + strategy: DisjointUnionStrategy[Tiling, GriddedPerm], + param_idx: int, + preimg_idx: int, + ): + assert isinstance(strategy, DisjointUnionStrategy) + self.strategy = strategy + self.param_idx = param_idx + self.preimg_idx = preimg_idx + super().__init__(ignore_parent=True) + + def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: + if ( + len(comb_class.parameters) <= self.param_idx + or len(comb_class.parameters[self.param_idx].counters) <= self.preimg_idx + ): + raise StrategyDoesNotApply + params: List[List[PreimageCounter]] = [] + for param_idx, param in enumerate(comb_class.parameters): + params.append([]) + for preimg_idx, preimg in enumerate(param.counters): + if param_idx == self.param_idx and preimg_idx == self.preimg_idx: + t = preimg.tiling + rule = self.strategy(t) + assert isinstance(rule, Rule) + if not isinstance(rule.constructor, DisjointUnion): + raise StrategyDoesNotApply + for child in rule.children: + params[-1].append(PreimageCounter(child, preimg.map)) + else: + params[-1].append(preimg) + t = comb_class.remove_parameters().add_parameters(map(ParameterCounter, params)) + return (t,) + + # def backward_map( + # self, + # comb_class: Tiling, + # objs: Tuple[Optional[GriddedPerm], ...], + # children: Optional[Tuple[Tiling, ...]] = None, + # ) -> Iterator[GriddedPerm]: + # return objs[0] + + def forward_map( + self, + comb_class: Tiling, + obj: GriddedPerm, + children: Optional[Tuple[Tiling, ...]] = None, + ) -> Tuple[Optional[GriddedPerm], ...]: + return (obj,) + + def formal_step(self) -> str: + return ( + f"applied '{self.strategy.formal_step()}' to primage " + f"{self.preimg_idx} in parameter {self.param_idx}" + ) + + @classmethod + def from_dict(cls, d: dict) -> "DisjointParameterStrategy": + strategy = AbstractStrategy.from_dict(d.pop("strategy")) + assert isinstance(strategy, DisjointUnionStrategy) + return cls(strategy, **d) + + def to_jsonable(self) -> dict: + d = super().to_jsonable() + d["strategy"] = self.strategy.to_jsonable() + d["param_idx"] = self.param_idx + d["preimg_idx"] = self.preimg_idx + return d + + +class DisjointUnionParameterFactory(StrategyFactory[Tiling]): + def __init__(self, strategy: CSSstrategy): + self.strategy = strategy + super().__init__() + + def __call__(self, comb_class: Tiling) -> Iterator[DisjointUnionStrategy]: + for i, param in enumerate(comb_class.parameters): + for j, preimage in enumerate(param.counters): + for rule in CombinatorialSpecificationSearcher._rules_from_strategy( + preimage.tiling, self.strategy + ): + assert isinstance(rule.strategy, DisjointUnionStrategy) + yield DisjointParameterStrategy(rule.strategy, i, j) + + def __str__(self) -> str: + return f"applying '{self.strategy}' to parameters" + + def __repr__(self) -> str: + return f"DisjointUnionParameterFactory({self.strategy!r})" + + def to_jsonable(self) -> dict: + d = super().to_jsonable() + d["strategy"] = self.strategy.to_jsonable() + return d + + @classmethod + def from_dict(cls, d: dict) -> "DisjointUnionParameterFactory": + strategy = AbstractStrategy.from_dict(d.pop("strategy")) + assert not d + return cls(strategy) + + +class ParameterVerificationStrategy(VerificationStrategy[Tiling, GriddedPerm]): + """ + A subclass for when a combinatorial class is equal to the empty set. + """ + + @staticmethod + def random_sample_object_of_size( + comb_class: Tiling, n: int, **parameters: int + ) -> GriddedPerm: + raise NotImplementedError + + @staticmethod + def verified(comb_class: Tiling) -> bool: + if ( + comb_class.dimensions != (1, 1) + or len(comb_class.parameters) != 1 + or len(comb_class.parameters[0].counters) != 1 + ): + return False + preimage = comb_class.parameters[0].counters[0] + if not sum(preimage.tiling.dimensions) == 3: + return False + extra_obs, extra_reqs = preimage.extra_obs_and_reqs(comb_class) + # TODO: check if skew, sum, or point fusion. + # TODO: Should child be without params? + return not extra_reqs and ( + all(len(ob) < 3 and not ob.is_single_cell() for ob in extra_obs) + or not extra_obs + ) + + @staticmethod + def formal_step() -> str: + return "parameter verified" + + @staticmethod + def pack(comb_class: Tiling) -> StrategyPack: + raise NotImplementedError + + @classmethod + def from_dict(cls, d: dict) -> "ParameterVerificationStrategy": + assert not d + return cls() + + def to_jsonable(self) -> dict: + d = super().to_jsonable() + d.pop("ignore_parent") + return d + + def __str__(self) -> str: + return "parameter verification" diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index 9f551261..ae8efd48 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -12,6 +12,11 @@ from permuta import Perm from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS from tilings import strategies as strat +from tilings.strategies.parameter_strategies import ( + DisjointUnionParameterFactory, + ParameterVerificationStrategy, + RemoveIdentityPreimageStrategy, +) from tilings.strategies.verification import BasisAwareVerificationStrategy if TYPE_CHECKING: @@ -127,18 +132,14 @@ def replace_list(strats): res.append(strategy) return res - return ( - self.__class__( - ver_strats=replace_list(self.ver_strats), - inferral_strats=replace_list(self.inferral_strats), - initial_strats=replace_list(self.initial_strats), - expansion_strats=list(map(replace_list, self.expansion_strats)), - name=self.name, - symmetries=self.symmetries, - iterative=self.iterative, - ) - .add_initial(strat.AddAssumptionFactory(), apply_first=True) - .add_initial(strat.RearrangeAssumptionFactory(), apply_first=True) + return self.__class__( + ver_strats=replace_list(self.ver_strats), + inferral_strats=replace_list(self.inferral_strats), + initial_strats=replace_list(self.initial_strats), + expansion_strats=list(map(replace_list, self.expansion_strats)), + name=self.name, + symmetries=self.symmetries, + iterative=self.iterative, ) def make_fusion( @@ -166,10 +167,12 @@ def make_fusion( ) pack = self.add_initial(fusion_strat, name, apply_first=apply_first) if tracked: - pack = pack.add_initial(strat.AddAssumptionFactory(), apply_first=True) pack = pack.add_initial( - strat.RearrangeAssumptionFactory(), apply_first=True + DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + apply_first=True, ) + pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) + pack = pack.add_verification(ParameterVerificationStrategy()) return pack def make_interleaving( @@ -215,8 +218,12 @@ def replace_list(strats): pack = pack.add_initial( strat.AddInterleavingAssumptionFactory(unions=unions), apply_first=True ) - pack = pack.add_initial(strat.AddAssumptionFactory(), apply_first=True) - + pack = pack.add_initial( + DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + apply_first=True, + ) + pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) + pack = pack.add_verification(ParameterVerificationStrategy()) return pack def make_elementary(self) -> "TileScopePack": From ec22d377a8dc660c2dd808020791e431ab649668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 18 Nov 2021 13:26:35 +0000 Subject: [PATCH 24/41] Counting (#388) * some counting fixes * remove identity preimage counting * ensure preimages map to unique child in factor * fix remove_parameters typing * fusion account for requirements * fix a bug in fusion counting * RemoveParamReq factory and add identity constructor * add remove req factory to strategy packs * row -> col * fixing sort for preimage counters * counting av1234 by making fused preimage appear at most once in a parameter * remove test- it was false :) * fix parameter disjoint strategy * jays alt fusion, left/right param determined only by preimage of maps * row -> col * Revert "row -> col" This reverts commit 97e369580bc024fa4cb285d84322dcf15a7f3fca. * Revert "jays alt fusion, left/right param determined only by preimage of maps" This reverts commit fcf3b209e96463dedfd97f7ecda070573968ed9e. * mypy and pylint * json method for remove req factory * add some typing * some typing * test for fusion strat * limit fusion to normal fusion * clean test_assumption so we can reactivate it * reactivate some tests * remove unused imports * skip a test instead of xfailing * remove comented code * apply child forward map in extra parameters of fusion Co-authored-by: Christian Bean --- mypy.ini | 26 +- tests/strategies/test_fusion_strat.py | 9 +- tests/test_assumptions.py | 158 ++++++--- tests/test_tilescope.py | 2 +- tilings/algorithms/factor.py | 12 +- tilings/algorithms/fusion.py | 100 +++++- tilings/map.py | 3 + tilings/parameter_counter.py | 4 +- tilings/strategies/fusion/constructor.py | 26 +- tilings/strategies/fusion/fusion.py | 56 ++-- tilings/strategies/parameter_strategies.py | 362 +++++++++++++++++++-- tilings/strategy_pack.py | 3 + tilings/tiling.py | 3 +- tox.ini | 3 +- 14 files changed, 615 insertions(+), 152 deletions(-) diff --git a/mypy.ini b/mypy.ini index 3f150d27..5a0d9caa 100644 --- a/mypy.ini +++ b/mypy.ini @@ -11,29 +11,27 @@ ignore_errors = True ignore_missing_imports = True # Temporary addition. Should be removed before going in develop -[mypy-tilings.strategies.*] -ignore_errors = True -[mypy-tilings.algorithms.fusion] +[mypy-tilings.algorithms.sliding] ignore_errors = True -[mypy-tilings.algorithms.general_fusion] +[mypy-tilings.bijections] ignore_errors = True -[mypy-tilings.algorithms.sliding] +[mypy-tilings.strategies.assumption_insertion] ignore_errors = True -[mypy-tilings.bijections] +[mypy-tilings.strategies.symmetry] ignore_errors = True -[mypy-tilings.strategies.row_and_col_separation] -ignore_errors = False +[mypy-tilings.strategies.sliding] +ignore_errors = True -[mypy-tilings.strategies.requirement_placement] -ignore_errors = False +[mypy-tilings.strategies.assumption_splitting] +ignore_errors = True -[mypy-tilings.strategies.factor] -ignore_errors = False +[mypy-tilings.strategies.rearrange_assumption] +ignore_errors = True -[mypy-tilings.strategies.parameter_strategies] -ignore_errors = False +[mypy-tilings.strategies.detect_components] +ignore_errors = True diff --git a/tests/strategies/test_fusion_strat.py b/tests/strategies/test_fusion_strat.py index f646d559..ccad0e6f 100644 --- a/tests/strategies/test_fusion_strat.py +++ b/tests/strategies/test_fusion_strat.py @@ -111,6 +111,7 @@ def big_tiling(): return t +@pytest.mark.xfail def test_fusion(small_tiling, big_tiling): assert len(list(FusionFactory()(big_tiling))) == 0 small_tiling_rules = list(FusionFactory()(small_tiling)) @@ -212,6 +213,7 @@ def test_formal_step_component(component_col_fusion, component_row_fusion): assert isinstance(component_col_fusion.constructor, FusionConstructor) +@pytest.mark.xfail # double positive fusion def test_fuse_parameter(): tiling = Tiling( obstructions=( @@ -243,14 +245,14 @@ def test_fuse_parameter(): GriddedPerm((1, 0, 2), ((3, 2), (3, 2), (3, 2))), ), requirements=((GriddedPerm((0,), ((2, 2),)), GriddedPerm((0,), ((3, 2),))),), - assumptions=(), ) strategy = FusionStrategy(col_idx=2, tracked=True) - assert strategy._fuse_parameter(tiling) == "k_0" rule = strategy(tiling) + assert strategy._fuse_parameter_name(tiling) == "k_0" assert isinstance(rule.constructor, FusionConstructor) +@pytest.mark.xfail def test_positive_fusion(): tiling = Tiling( [ @@ -350,6 +352,7 @@ def easy_fusable( return tiling +@pytest.mark.xfail def test_fusion_gfs(): x = var("x") @@ -491,6 +494,7 @@ def eq_equality(e1, e2): # in each cell. Equations for this are not currently implemented. +@pytest.mark.xfail def test_indexed_forward_map(): assert FusionStrategy(col_idx=0, tracked=True)( Tiling( @@ -521,6 +525,7 @@ def test_indexed_forward_map(): ) +@pytest.mark.xfail def test_indexed_backward_map(): r = FusionStrategy(col_idx=0, tracked=True)( Tiling( diff --git a/tests/test_assumptions.py b/tests/test_assumptions.py index b37737e9..025f8998 100644 --- a/tests/test_assumptions.py +++ b/tests/test_assumptions.py @@ -7,8 +7,8 @@ from comb_spec_searcher import CombinatorialSpecification from permuta import Av, Perm from tilings import GriddedPerm, Tiling -from tilings.algorithms import Factor -from tilings.assumptions import TrackingAssumption +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategy_pack import TileScopePack from tilings.tilescope import TileScope @@ -31,25 +31,54 @@ def tplaced_factored2(tplaced): @pytest.fixture def tplaced_tracked(tplaced): - return Tiling( - tplaced.obstructions, - tplaced.requirements, - [ - TrackingAssumption([GriddedPerm.single_cell((0,), (0, 0))]), - TrackingAssumption([GriddedPerm.single_cell((0,), (0, 0))]), - TrackingAssumption([GriddedPerm.single_cell((0,), (2, 0))]), - ], + preimg0 = PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (3, 0))), + GriddedPerm((0, 1), ((1, 0), (3, 0))), + GriddedPerm((0, 1), ((2, 1), (2, 1))), + GriddedPerm((1, 0), ((2, 1), (2, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 0), (3, 0))), + ), + requirements=((GriddedPerm((0,), ((2, 1),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1}, {0: 0, 1: 0, 2: 1, 3: 2}), + ) + preimg2 = PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (2, 0))), + GriddedPerm((0, 1), ((0, 0), (3, 0))), + GriddedPerm((0, 1), ((1, 1), (1, 1))), + GriddedPerm((1, 0), ((1, 1), (1, 1))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((2, 0), (2, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((2, 0), (2, 0), (3, 0))), + GriddedPerm((0, 2, 1), ((2, 0), (3, 0), (3, 0))), + GriddedPerm((0, 2, 1), ((3, 0), (3, 0), (3, 0))), + ), + requirements=((GriddedPerm((0,), ((1, 1),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1}, {0: 0, 1: 1, 2: 2, 3: 2}), ) - -@pytest.fixture -def tplaced_tracked_factored1(tplaced_tracked): - return tplaced_tracked.sub_tiling([(0, 0), (2, 0)]) + return tplaced.add_parameters( + [ + ParameterCounter([preimg0]), + ParameterCounter([preimg2]), + ] + ) @pytest.fixture -def tplaced_tracked_factored2(tplaced_tracked): - return tplaced_tracked.sub_tiling([(1, 1)]) +def tplaced_tracked_factors(tplaced_tracked): + return tplaced_tracked.find_factors() @pytest.fixture @@ -58,19 +87,19 @@ def all_tilings( tplaced_factored1, tplaced_factored2, tplaced_tracked, - tplaced_tracked_factored1, - tplaced_tracked_factored2, + tplaced_tracked_factors, ): - return [ + tilings = [ tplaced, tplaced_factored1, tplaced_factored2, tplaced_tracked, - tplaced_tracked_factored1, - tplaced_tracked_factored2, ] + tilings.extend(tplaced_tracked_factors) + return tilings +@pytest.mark.xfail def test_bytes(tplaced, tplaced_tracked, all_tilings): assert len(tplaced.assumptions) == 0 @@ -84,40 +113,57 @@ def test_bytes(tplaced, tplaced_tracked, all_tilings): assert remade == tiling +@pytest.mark.xfail def test_json(all_tilings): for tiling in all_tilings: assert Tiling.from_json(json.dumps(tiling.to_jsonable())) == tiling -def test_factors(tplaced_tracked, tplaced_tracked_factored1, tplaced_tracked_factored2): - assert len(tplaced_tracked_factored1.assumptions) == 2 - - assert all( - isinstance(ass, TrackingAssumption) - for ass in tplaced_tracked_factored1.assumptions - ) - assert tplaced_tracked_factored1.assumptions[0].gps == ( - GriddedPerm.single_cell((0,), (0, 0)), - ) - assert tplaced_tracked_factored1.assumptions[1].gps == ( - GriddedPerm.single_cell((0,), (1, 0)), - ) - - assert set(Factor(tplaced_tracked).factors()) == set( - [tplaced_tracked_factored1, tplaced_tracked_factored2] - ) - - -def test_from_cell(): - assert TrackingAssumption.from_cells([]) == TrackingAssumption([]) - assert TrackingAssumption.from_cells([(0, 1)]) == TrackingAssumption( - [GriddedPerm((0,), [(0, 1)])] +def test_factors(tplaced_tracked, tplaced_tracked_factors): + assert sorted(len(f.parameters) for f in tplaced_tracked_factors) == [0, 2] + + main_factor = next(f for f in tplaced_tracked_factors if f.dimensions == (2, 1)) + + assert all(isinstance(ass, ParameterCounter) for ass in main_factor.parameters) + assert main_factor.parameters[0] == ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((0, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((2, 0), (2, 0), (2, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 1}), + ), + ) ) - assert TrackingAssumption.from_cells([(0, 1), (2, 3)]) == TrackingAssumption( - [ - GriddedPerm((0,), [(0, 1)]), - GriddedPerm((0,), [(2, 3)]), - ] + assert main_factor.parameters[1] == ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (2, 0))), + GriddedPerm((0, 1), ((1, 0), (2, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (0, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((1, 0), (1, 0), (1, 0))), + GriddedPerm((0, 2, 1), ((2, 0), (2, 0), (2, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0, 2: 1}), + ), + ) ) @@ -150,7 +196,17 @@ def test_123_fusion(): 477638700, 1767263190, ] + + +@pytest.mark.xfail +@pytest.mark.timeout(90) +def test_123_fusion_generate_and_sample(): av = Av([Perm((0, 1, 2))]) + pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) + css = TileScope("123", pack) + spec = css.auto_search(status_update=30) + spec = spec.expand_verified() + assert isinstance(spec, CombinatorialSpecification) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i) @@ -158,6 +214,7 @@ def test_123_fusion(): assert spec.random_sample_object_of_size(i).patt in av +@pytest.mark.skip(reason="positive fusion not implemented") @pytest.mark.timeout(60) def test_123_positive_fusions(): pack = TileScopePack.insertion_row_and_col_placements(row_only=True).make_fusion( @@ -198,6 +255,7 @@ def test_123_positive_fusions(): assert spec.random_sample_object_of_size(i).patt in av +@pytest.mark.skip(reason="interleaving factor not implemented") @pytest.mark.timeout(60) def test_123_interleaving(): pack = TileScopePack.point_placements().make_interleaving() @@ -229,6 +287,7 @@ def test_123_interleaving(): ] +@pytest.mark.xfail @pytest.mark.timeout(120) def test_1234_fusion(): __location__ = os.path.realpath( @@ -264,6 +323,7 @@ def test_1234_fusion(): assert spec.random_sample_object_of_size(i).patt in av +@pytest.mark.xfail def test_1234_pickle(): """ Test that the specification can be pickled. diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index 34a1fdb3..9e7c89ee 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -383,7 +383,7 @@ def forest_expansion(): ] -@pytest.mark.xfail +@pytest.mark.skip(reason="seems to run forever") def test_guided_searcher(): tilescope = TileScope( "123", TileScopePack.point_placements().make_fusion(tracked=False) diff --git a/tilings/algorithms/factor.py b/tilings/algorithms/factor.py index 02b6e3a4..5686a8d8 100644 --- a/tilings/algorithms/factor.py +++ b/tilings/algorithms/factor.py @@ -1,3 +1,4 @@ +import itertools from collections import defaultdict from itertools import chain, combinations from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, Optional, Set, Tuple @@ -177,7 +178,16 @@ def factorable(self) -> bool: """ Returns `True` if the tiling has more than one factor. """ - return len(self.get_components()) > 1 + return ( + all( + active_region + for active_region in itertools.chain.from_iterable( + param.active_regions(self._tiling) + for param in self._tiling.parameters + ) + ) # ensures that each preimage is mapped to a unique child + and len(self.get_components()) > 1 + ) def factor(self, component: Set[Cell]) -> "Tiling": """ diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 8021e9c7..aaefba33 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -1,7 +1,7 @@ """ The implementation of the fusion algorithm """ - +import itertools from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple from tilings.griddedperm import GriddedPerm @@ -20,8 +20,8 @@ class Fusion: - MAX_NUMBER_EXTRA = 1 - MAX_LENGTH_EXTRA = 2 + MAX_NUMBER_EXTRA = 0 + MAX_LENGTH_EXTRA = 0 MAX_NUM_PARAMS = 1 def __init__( @@ -59,7 +59,7 @@ def _fuse_map(self) -> RowColMap: col_map[i] = i - 1 return RowColMap(row_map, col_map) - def _fuse_gps(self, gps: Iterable["GriddedPerm"]): + def _fuse_gps(self, gps: Iterable["GriddedPerm"]) -> List[GriddedPerm]: return self.upward_closure(self.fuse_map.map_gps(gps)) @staticmethod @@ -80,7 +80,7 @@ def fused_obs_reqs_and_params(self) -> UninitializedTiling: tuple(self.fused_param(param) for param in self.tiling.parameters), ) - def is_fusable_param(self, parameter_counter: ParameterCounter): + def is_fusable_param(self, parameter_counter: ParameterCounter) -> bool: return all( self.is_fusable_preimage(preimage) for preimage in parameter_counter.counters @@ -95,7 +95,7 @@ def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: self.tiling.cells_in_row(self._row_idx + 1) ) else: - fuse_region = self.tiling.cells_in_row(self._col_idx).union( + fuse_region = self.tiling.cells_in_col(self._col_idx).union( self.tiling.cells_in_col(self._col_idx + 1) ) if preimage.active_region(self.tiling).intersection(fuse_region): @@ -133,9 +133,7 @@ def fused_preimage(self, preimage: PreimageCounter) -> PreimageCounter: row_idx = row1 else: col_idx = row1 - fuse_algo = Fusion( - preimage.tiling.remove_requirements(), row_idx, col_idx, False - ) + fuse_algo = Fusion(preimage.tiling, row_idx, col_idx, False) fused_tiling = fuse_algo.fused_tiling() fused_map = RowColMap( { @@ -150,9 +148,20 @@ def fused_preimage(self, preimage: PreimageCounter) -> PreimageCounter: return PreimageCounter(fused_tiling, fused_map) def fused_param(self, parameter: ParameterCounter) -> ParameterCounter: - return ParameterCounter( - [self.fused_preimage(preimage) for preimage in parameter.counters] - ) + counters = [self.fused_preimage(preimage) for preimage in parameter.counters] + newpreimage = self.new_parameter().counters[0] + # The following ensures that the new preimage appears at most once in a + # parameter. + removed = False + while True: + try: + counters.remove(newpreimage) + removed = True + except ValueError: + break + if removed: + counters.append(newpreimage) + return ParameterCounter(counters) def unfused_fused_obs_reqs_and_params(self) -> UninitializedTiling: """ @@ -165,7 +174,7 @@ def unfused_fused_obs_reqs_and_params(self) -> UninitializedTiling: tuple(), ) - def fusable(self): + def fusable(self) -> bool: """ Return True if tiling is fusable. """ @@ -195,7 +204,7 @@ def fused_tiling(self) -> "Tiling": params += (self.new_parameter(),) return self.tiling.__class__(obs, reqs, params) - def new_parameter(self): + def new_parameter(self) -> ParameterCounter: """ Return the parameter needed in order to count the fusion. """ @@ -203,6 +212,69 @@ def new_parameter(self): [PreimageCounter(self.tiling.remove_parameters(), self.fuse_map)] ) + def min_left_right_points(self) -> Tuple[int, int]: + """Return if the left side is positive, else 0 otherwise.""" + left, right = 0, 0 + if self._fuse_row: + if all( + cell in self.tiling.positive_cells + for cell in self.tiling.cells_in_row(self._row_idx) + ): + left += 1 + if all( + cell in self.tiling.positive_cells + for cell in self.tiling.cells_in_row(self._row_idx + 1) + ): + right += 1 + else: + if all( + cell in self.tiling.positive_cells + for cell in self.tiling.cells_in_col(self._col_idx) + ): + left += 1 + if all( + cell in self.tiling.positive_cells + for cell in self.tiling.cells_in_col(self._col_idx + 1) + ): + right += 1 + return left, right + + def is_left_sided_parameter(self, parameter: ParameterCounter) -> bool: + """ + Return True if active region doesn't overlap the right column/row being fused + """ + if self._fuse_row: + return all( + y != self._row_idx + 1 + for _, y in itertools.chain.from_iterable( + parameter.active_regions(self.tiling) + ) + ) + return all( + x != self._col_idx + 1 + for x, _ in itertools.chain.from_iterable( + parameter.active_regions(self.tiling) + ) + ) + + def is_right_sided_parameter(self, parameter: ParameterCounter) -> bool: + """ + Return True if active region doesn't overlap the left column/row being fused + """ + if self._fuse_row: + return all( + y != self._row_idx + for _, y in itertools.chain.from_iterable( + parameter.active_regions(self.tiling) + ) + ) + return all( + x != self._col_idx + for x, _ in itertools.chain.from_iterable( + parameter.active_regions(self.tiling) + ) + ) + def extra_obs_and_reqs( self, ) -> Tuple[List[GriddedPerm], List[Tuple[GriddedPerm, ...]]]: diff --git a/tilings/map.py b/tilings/map.py index 00735319..7691d45b 100644 --- a/tilings/map.py +++ b/tilings/map.py @@ -204,6 +204,9 @@ def __eq__(self, other: object) -> bool: return NotImplemented return self._map == other._map + def __lt__(self, other): + return tuple(sorted(self._map.items())) < tuple(sorted(other._map.items())) + def __hash__(self) -> int: return hash(tuple(sorted(self._map.items()))) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 9e9c233a..2c79183f 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -131,16 +131,14 @@ def __lt__(self, other: object) -> bool: if not isinstance(other, PreimageCounter): return NotImplemented key_self = ( - self.__class__.__name__, self.tiling.obstructions, self.tiling.requirements, self.map, ) key_other = ( - other.__class__.__name__, other.tiling.obstructions, other.tiling.requirements, - self.map, + other.map, ) return key_self < key_other diff --git a/tilings/strategies/fusion/constructor.py b/tilings/strategies/fusion/constructor.py index 6e9d0592..b04dff2e 100644 --- a/tilings/strategies/fusion/constructor.py +++ b/tilings/strategies/fusion/constructor.py @@ -129,6 +129,11 @@ def __init__( for i, k in enumerate(parent.extra_parameters) if k in self.right_sided_parameters ) + self.both_parameter_indices = tuple( + i + for i, k in enumerate(parent.extra_parameters) + if k in self.both_sided_parameters + ) self.fuse_parameter_index = child.extra_parameters.index(self.fuse_parameter) child_pos_to_parent_pos = tuple( index_mapping[idx] for idx in range(len(child.extra_parameters)) @@ -253,28 +258,31 @@ def get_terms( min_left, min_right = self.min_points def add_new_term( - params: List[int], value: int, left_points: int, fuse_region_points: int + params: List[int], value: int, left_points: int, fuse_region_griddings: int ) -> None: """Update new terms if there is enough points on the left and right.""" if ( - min_left <= left_points - and min_right <= fuse_region_points - left_points - ): + min_left <= fuse_region_griddings - 1 + and min_right <= fuse_region_griddings - left_points + 1 + ): # <- NEW: should be a -1, since 1 gridding implies 0 points in our logic new_terms[tuple(params)] += value for param, value in subterms[0](n).items(): - fuse_region_points = param[self.fuse_parameter_index] + fuse_region_griddings = param[self.fuse_parameter_index] new_params = list(self.children_param_map(param)) for idx in self.left_parameter_indices: - new_params[idx] -= fuse_region_points - add_new_term(new_params, value, 0, fuse_region_points) - for left_points in range(1, fuse_region_points + 1): + new_params[idx] -= fuse_region_griddings + for idx in self.right_parameter_indices: + new_params[idx] += 1 + for idx in self.both_parameter_indices: + new_params[idx] += 1 + for left_points in range(1, fuse_region_griddings + 1): for idx in self.left_parameter_indices: new_params[idx] += 1 for idx in self.right_parameter_indices: new_params[idx] -= 1 - add_new_term(new_params, value, left_points, fuse_region_points) + add_new_term(new_params, value, left_points, fuse_region_griddings) return new_terms def determine_number_of_points_in_fuse_region( diff --git a/tilings/strategies/fusion/fusion.py b/tilings/strategies/fusion/fusion.py index 3f9837ac..78044321 100644 --- a/tilings/strategies/fusion/fusion.py +++ b/tilings/strategies/fusion/fusion.py @@ -172,12 +172,9 @@ def is_two_way(comb_class: Tiling): def is_reversible(self, comb_class: Tiling) -> bool: algo = self.fusion_algorithm(comb_class) - new_ass = algo.new_assumption() - fused_assumptions = ( - ass.__class__(gps) - for ass, gps in zip(comb_class.assumptions, algo.assumptions_fuse_counters) - ) - return new_ass in fused_assumptions + new_param = algo.new_parameter() + fused_params = map(algo.fused_param, comb_class.parameters) + return new_param in fused_params @staticmethod def shifts( @@ -201,7 +198,7 @@ def constructor( return FusionConstructor( comb_class, child, - self._fuse_parameter(comb_class), + self._fuse_parameter_name(comb_class), self.extra_parameters(comb_class, children)[0], *self.left_right_both_sided_parameters(comb_class), min_left, @@ -235,7 +232,7 @@ def reverse_constructor( # pylint: disable=no-self-use return ReverseFusionConstructor( comb_class, child, - self._fuse_parameter(comb_class), + self._fuse_parameter_name(comb_class), self.extra_parameters(comb_class, children)[0], tuple(left_sided_params), tuple(right_sided_params), @@ -250,15 +247,18 @@ def extra_parameters( raise StrategyDoesNotApply("Strategy does not apply") algo = self.fusion_algorithm(comb_class) child = children[0] - mapped_assumptions = [ - child.forward_map.map_assumption(ass.__class__(gps)) - for ass, gps in zip(comb_class.assumptions, algo.assumptions_fuse_counters) - ] + ( + _, + _, + mapped_parameters, + ) = algo.fused_obs_reqs_and_params() + mapped_parameters = tuple( + param.apply_row_col_map(child.forward_map) for param in mapped_parameters + ) return ( { - k: child.get_assumption_parameter(ass) - for k, ass in zip(comb_class.extra_parameters, mapped_assumptions) - if ass.gps + k: child.get_parameter_name(param) + for k, param in zip(comb_class.extra_parameters, mapped_parameters) }, ) @@ -269,10 +269,10 @@ def left_right_both_sided_parameters( right_sided_params: Set[str] = set() both_sided_params: Set[str] = set() algo = self.fusion_algorithm(comb_class) - for assumption in comb_class.assumptions: - parent_var = comb_class.get_assumption_parameter(assumption) - left_sided = algo.is_left_sided_assumption(assumption) - right_sided = algo.is_right_sided_assumption(assumption) + for parameter in comb_class.parameters: + parent_var = comb_class.get_parameter_name(parameter) + left_sided = algo.is_left_sided_parameter(parameter) + right_sided = algo.is_right_sided_parameter(parameter) if left_sided and not right_sided: left_sided_params.add(parent_var) elif right_sided and not left_sided: @@ -285,12 +285,12 @@ def left_right_both_sided_parameters( both_sided_params, ) - def _fuse_parameter(self, comb_class: Tiling) -> str: + def _fuse_parameter_name(self, comb_class: Tiling) -> str: + """Return the parameter name used by the fuse parameter.""" algo = self.fusion_algorithm(comb_class) child = algo.fused_tiling() - ass = algo.new_assumption() - fuse_assumption = ass.__class__(child.forward_map.map_gp(gp) for gp in ass.gps) - return child.get_assumption_parameter(fuse_assumption) + ass = algo.new_parameter() + return child.get_parameter_name(ass) def formal_step(self) -> str: fusing = "rows" if self.row_idx is not None else "columns" @@ -311,12 +311,7 @@ def backward_map( """ if children is None: children = self.decomposition_function(comb_class) - gp = objs[0] - assert gp is not None - gp = children[0].backward_map.map_gp(gp) - yield from self.fusion_algorithm(comb_class).unfuse_gridded_perm( - gp, left_points - ) + raise NotImplementedError def forward_map( self, @@ -330,8 +325,7 @@ def forward_map( """ if children is None: children = self.decomposition_function(comb_class) - fused_gp = self.fusion_algorithm(comb_class).fuse_gridded_perm(obj) - return (children[0].forward_map.map_gp(fused_gp),) + raise NotImplementedError def to_jsonable(self) -> dict: d = super().to_jsonable() diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 44704333..61105306 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -1,4 +1,7 @@ -from typing import Iterator, List, Optional, Tuple +from collections import Counter +from typing import Callable, Dict, Iterator, List, Optional, Tuple, cast + +import sympy from comb_spec_searcher import CombinatorialSpecificationSearcher from comb_spec_searcher.exception import StrategyDoesNotApply @@ -13,9 +16,191 @@ StrategyPack, VerificationStrategy, ) -from comb_spec_searcher.typing import CSSstrategy +from comb_spec_searcher.typing import ( + CSSstrategy, + Parameters, + ParametersMap, + RelianceProfile, + SubObjects, + SubRecs, + SubSamplers, + SubTerms, + Terms, +) from tilings import GriddedPerm, Tiling from tilings.parameter_counter import ParameterCounter, PreimageCounter +from tilings.strategies.requirement_insertion import RequirementInsertionStrategy + + +class RemoveIdentityPreimageConstructor(Constructor): + def __init__( + self, + parent: Tiling, + child: Tiling, + extra_params: Dict[str, str], + reduction: Dict[str, int], + ): + self.child_to_parent_map = self.remove_param_map( + parent, child, extra_params, reduction + ) + + @staticmethod + def remove_param_map( + parent: Tiling, + child: Tiling, + extra_params: Dict[str, str], + reduction: Dict[str, int], + ) -> ParametersMap: + """ + Returns a function that transform the parameters on the child to parameters on + the parent of the rule. + """ + map_data_partial: List[Optional[Tuple[int, int]]] = list( + None for _ in parent.parameters + ) + for parent_param_name, child_param_name in extra_params.items(): + child_param_idx = child.parameters.index( + child.get_parameter(child_param_name) + ) + parent_param_idx = parent.parameters.index( + parent.get_parameter(parent_param_name) + ) + map_data_partial[parent_param_idx] = ( + child_param_idx, + reduction[parent_param_name], + ) + assert all(x is not None for x in map_data_partial) + map_data: Tuple[Tuple[int, int], ...] = tuple( + cast(List[Tuple[int, int]], map_data_partial) + ) + + def param_map(param: Parameters) -> Parameters: + return tuple( + param[child_param_idx] + reduction + for child_param_idx, reduction in map_data + ) + + return param_map + + def get_equation( + self, lhs_func: sympy.Function, rhs_funcs: Tuple[sympy.Function, ...] + ) -> sympy.Eq: + raise NotImplementedError + + def reliance_profile(self, n: int, **parameters: int) -> RelianceProfile: + raise NotImplementedError + + def get_terms( + self, parent_terms: Callable[[int], Terms], subterms: SubTerms, n: int + ) -> Terms: + terms: Terms = Counter() + for param, count in subterms[0](n).items(): + new_param = self.child_to_parent_map(param) + terms[new_param] += count + return terms + + def get_sub_objects( + self, subobjs: SubObjects, n: int + ) -> Iterator[Tuple[Parameters, Tuple[List[Optional[GriddedPerm]], ...]]]: + raise NotImplementedError + + def random_sample_sub_objects( + self, + parent_count: int, + subsamplers: SubSamplers, + subrecs: SubRecs, + n: int, + **parameters: int, + ) -> Tuple[Optional[GriddedPerm], ...]: + raise NotImplementedError + + def equiv( + self, other: Constructor, data: Optional[object] = None + ) -> Tuple[bool, Optional[object]]: + raise NotImplementedError + + +class AddIdentityPreimageConstructor(Constructor): + def __init__( + self, + parent: Tiling, + child: Tiling, + extra_params: Dict[str, str], + reduction: Dict[str, int], + ): + self.child_to_parent_map = self.add_identity_param_map( + parent, child, extra_params, reduction + ) + + @staticmethod + def add_identity_param_map( + parent: Tiling, + child: Tiling, + extra_params: Dict[str, str], + reduction: Dict[str, int], + ) -> ParametersMap: + reverse_extra_params = {v: k for k, v in extra_params.items()} + map_data_partial: List[Optional[Tuple[int, int]]] = list( + None for _ in child.parameters + ) + for child_param, parent_param in reverse_extra_params.items(): + child_param_idx = child.parameters.index(child.get_parameter(child_param)) + parent_param_idx = parent.parameters.index( + parent.get_parameter(parent_param) + ) + map_data_partial[child_param_idx] = ( + parent_param_idx, + reduction[parent_param], + ) + assert all(x is not None for x in map_data_partial) + map_data: Tuple[Tuple[int, int], ...] = tuple( + cast(List[Tuple[int, int]], map_data_partial) + ) + + def param_map(param: Parameters) -> Parameters: + return tuple( + param[parent_param_idx] - reduction + for parent_param_idx, reduction in map_data + ) + + return param_map + + def get_equation( + self, lhs_func: sympy.Function, rhs_funcs: Tuple[sympy.Function, ...] + ) -> sympy.Eq: + raise NotImplementedError + + def reliance_profile(self, n: int, **parameters: int) -> RelianceProfile: + raise NotImplementedError + + def get_terms( + self, parent_terms: Callable[[int], Terms], subterms: SubTerms, n: int + ) -> Terms: + terms: Terms = Counter() + for param, count in subterms[0](n).items(): + new_param = self.child_to_parent_map(param) + terms[new_param] += count + return terms + + def get_sub_objects( + self, subobjs: SubObjects, n: int + ) -> Iterator[Tuple[Parameters, Tuple[List[Optional[GriddedPerm]], ...]]]: + raise NotImplementedError + + def random_sample_sub_objects( + self, + parent_count: int, + subsamplers: SubSamplers, + subrecs: SubRecs, + n: int, + **parameters: int, + ) -> Tuple[Optional[GriddedPerm], ...]: + raise NotImplementedError + + def equiv( + self, other: Constructor, data: Optional[object] = None + ) -> Tuple[bool, Optional[object]]: + raise NotImplementedError class RemoveIdentityPreimageStrategy(Strategy[Tiling, GriddedPerm]): @@ -24,26 +209,66 @@ def __init__(self) -> None: def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: applied = False - params: List[List[PreimageCounter]] = [] + params: List[ParameterCounter] = [] for param in comb_class.parameters: - params.append([]) - for preimg in param.counters: - if ( - preimg.map.is_identity() - and preimg.tiling == comb_class.remove_parameters() - ): - applied = True - else: - params[-1].append(preimg) + new_param = self._map_param(comb_class, param) + applied = applied or len(new_param.counters) < len(param.counters) + params.append(new_param) if not applied: raise StrategyDoesNotApply - t = comb_class.remove_parameters().add_parameters(map(ParameterCounter, params)) + t = comb_class.remove_parameters().add_parameters(params) return (t,) + @staticmethod + def _map_param(comb_class: Tiling, param: ParameterCounter) -> ParameterCounter: + """ + Map a parameters of comb_class by removing the identity parameters. + """ + preimgs = ( + preimg + for preimg in param.counters + if not ( + preimg.map.is_identity() + and preimg.tiling == comb_class.remove_parameters() + ) + ) + return ParameterCounter(preimgs) + + def extra_parameter(self, comb_class: Tiling, child: Tiling) -> Dict[str, str]: + """ + Indicate to which parameter on the child each parameter on the parent is + mapping. + """ + return { + comb_class.get_parameter_name(param): child.get_parameter_name( + self._map_param(comb_class, param) + ) + for param in comb_class.parameters + } + + def _param_reduction(self, comb_class) -> Dict[str, int]: + """ + For each of the param on comb_class, indicate how many identity preimages + have been removed. + """ + return { + comb_class.get_parameter_name(param): len(param.counters) + - len(self._map_param(comb_class, param).counters) + for param in comb_class.parameters + } + def constructor( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None ) -> Constructor: - raise NotImplementedError + if children is None: + children = self.decomposition_function(comb_class) + child = children[0] + return RemoveIdentityPreimageConstructor( + comb_class, + child, + self.extra_parameter(comb_class, child), + self._param_reduction(comb_class), + ) def reverse_constructor( self, @@ -51,7 +276,16 @@ def reverse_constructor( comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None, ) -> Constructor: - raise NotImplementedError + assert idx == 0 + if children is None: + children = self.decomposition_function(comb_class) + child = children[0] + return AddIdentityPreimageConstructor( + comb_class, + child, + self.extra_parameter(comb_class, child), + self._param_reduction(comb_class), + ) def backward_map( self, @@ -105,12 +339,13 @@ def __init__( strategy: DisjointUnionStrategy[Tiling, GriddedPerm], param_idx: int, preimg_idx: int, + ignore_parent: bool = True, ): assert isinstance(strategy, DisjointUnionStrategy) self.strategy = strategy self.param_idx = param_idx self.preimg_idx = preimg_idx - super().__init__(ignore_parent=True) + super().__init__(ignore_parent=ignore_parent) def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: if ( @@ -135,13 +370,43 @@ def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: t = comb_class.remove_parameters().add_parameters(map(ParameterCounter, params)) return (t,) - # def backward_map( - # self, - # comb_class: Tiling, - # objs: Tuple[Optional[GriddedPerm], ...], - # children: Optional[Tuple[Tiling, ...]] = None, - # ) -> Iterator[GriddedPerm]: - # return objs[0] + def extra_parameters( + self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None + ) -> Tuple[Dict[str, str], ...]: + if children is None: + children = self.decomposition_function(comb_class) + child = children[0] + extra_params: Dict[str, str] = {} + for i, param in enumerate(comb_class.parameters): + if i == self.param_idx: + continue + extra_params[ + comb_class.get_parameter_name(param) + ] = child.get_parameter_name(param) + param = comb_class.parameters[self.param_idx] + new_preimages = [] + for j, preimage in enumerate(param.counters): + if j != self.preimg_idx: + new_preimages.append(preimage) + continue + rule = self.strategy(preimage.tiling) + for preimage_child in rule.children: + new_preimages.append( + PreimageCounter(preimage_child, preimage.map) + ) # TODO: did the preimage map change? + new_parameter = ParameterCounter(new_preimages) + extra_params[comb_class.get_parameter_name(param)] = child.get_parameter_name( + new_parameter + ) + return (extra_params,) + + def backward_map( + self, + comb_class: Tiling, + objs: Tuple[Optional[GriddedPerm], ...], + children: Optional[Tuple[Tiling, ...]] = None, + ) -> Iterator[GriddedPerm]: + raise NotImplementedError def forward_map( self, @@ -153,7 +418,7 @@ def forward_map( def formal_step(self) -> str: return ( - f"applied '{self.strategy.formal_step()}' to primage " + f"applied '{self.strategy.formal_step()}' to preimage " f"{self.preimg_idx} in parameter {self.param_idx}" ) @@ -233,6 +498,9 @@ def verified(comb_class: Tiling) -> bool: or not extra_obs ) + def get_terms(self, comb_class: Tiling, n: int) -> Terms: + return comb_class.get_terms(n) + @staticmethod def formal_step() -> str: return "parameter verified" @@ -253,3 +521,49 @@ def to_jsonable(self) -> dict: def __str__(self) -> str: return "parameter verification" + + +class RemoveReqFactory(StrategyFactory[Tiling]): + def __call__(self, comb_class: Tiling) -> Iterator[Rule]: + for param_idx, param in enumerate(comb_class.parameters): + for preimg_idx, preimg in enumerate(param): + for req_idx, req in enumerate(preimg.tiling.requirements): + if ( + tuple(sorted((preimg.map.map_gps(req)))) + in comb_class.requirements + ): + continue + new_tiling = Tiling( + preimg.tiling.obstructions, + preimg.tiling.requirements[:req_idx] + + preimg.tiling.requirements[req_idx + 1 :], + ) + new_preimage = PreimageCounter(new_tiling, preimg.map) + new_param = ParameterCounter( + comb_class.parameters[param_idx].counters[:preimg_idx] + + comb_class.parameters[param_idx].counters[preimg_idx + 1 :] + + (new_preimage,) + ) + new_comb_class = comb_class.remove_parameter(param).add_parameter( + new_param + ) + param_strategy = RequirementInsertionStrategy(req) + strategy = DisjointParameterStrategy( + param_strategy, + new_comb_class.parameters.index(new_param), + new_param.counters.index(new_preimage), + False, + ) + rule = strategy(new_comb_class) + yield rule + + def __str__(self) -> str: + return "Remove requirements from preimages" + + def __repr__(self) -> str: + raise NotImplementedError + + @classmethod + def from_dict(cls, d: dict) -> "RemoveReqFactory": + assert not d + return cls() diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index ae8efd48..cc6781ef 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -16,6 +16,7 @@ DisjointUnionParameterFactory, ParameterVerificationStrategy, RemoveIdentityPreimageStrategy, + RemoveReqFactory, ) from tilings.strategies.verification import BasisAwareVerificationStrategy @@ -171,6 +172,7 @@ def make_fusion( DisjointUnionParameterFactory(strat.FactorInsertionFactory()), apply_first=True, ) + pack = pack.add_initial(RemoveReqFactory(), apply_first=True) pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) pack = pack.add_verification(ParameterVerificationStrategy()) return pack @@ -222,6 +224,7 @@ def replace_list(strats): DisjointUnionParameterFactory(strat.FactorInsertionFactory()), apply_first=True, ) + pack = pack.add_initial(RemoveReqFactory(), apply_first=True) pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) pack = pack.add_verification(ParameterVerificationStrategy()) return pack diff --git a/tilings/tiling.py b/tilings/tiling.py index 231ddcf9..05c8467a 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -624,9 +624,8 @@ def add_parameters(self, parameters: Iterable[ParameterCounter]) -> "Tiling": ) return tiling - def remove_parameter(self, parameter: Iterable[ParameterCounter]): + def remove_parameter(self, parameter: ParameterCounter): """Returns a new tiling with parameter removed.""" - parameter = tuple(sorted(set(parameter))) try: idx = self._parameters.index(parameter) except ValueError as e: diff --git a/tox.ini b/tox.ini index 99c2b551..73def4db 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/test_assumptions.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_fusion_strat.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE @@ -46,7 +46,6 @@ commands = [flake8] per-file-ignores = tilings/strategies/verification.py:F821 - tilings/algorithms/fusion.py:F821 tilings/strategies/assumption_splitting.py:F821 [testenv:pylint] From 40c199f8053ca40c38f010dd4f2b45ef7848cd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 18 Nov 2021 15:19:56 +0000 Subject: [PATCH 25/41] convert a test to new assumption --- tests/strategies/test_sanity_check.py | 2537 ++++++++++++++++++++++++- tox.ini | 2 +- 2 files changed, 2530 insertions(+), 9 deletions(-) diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index 9a241239..b8295f95 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -8,9 +8,7 @@ from tilings.map import RowColMap from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategies.factor import FactorStrategy -from tilings.strategies.fusion import FusionStrategy from tilings.strategies.obstruction_inferral import ObstructionInferralStrategy -from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy from tilings.strategies.requirement_insertion import RequirementInsertionStrategy from tilings.strategies.requirement_placement import RequirementPlacementStrategy @@ -1513,16 +1511,2539 @@ def test_eqv_path_complement(): GriddedPerm((0, 2, 4, 3, 1), ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0))), ), requirements=((GriddedPerm((0,), ((0, 3),)),),), - assumptions=( - TrackingAssumption((GriddedPerm((0,), ((0, 1),)),)), - TrackingAssumption( - (GriddedPerm((0,), ((0, 1),)), GriddedPerm((0,), ((0, 2),))) + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 1), (0, 4))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 4))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 1))), + GriddedPerm((1, 0), ((0, 4), (0, 2))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 4), (0, 5), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 4), (0, 5))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 4))), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 3), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 3), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 4), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 4), (0, 3), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 3), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 3), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 4), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 4), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 4), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 5), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 4), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 5), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 4), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 5), (0, 5), (0, 5), (0, 0)), + ), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 3),)), + GriddedPerm((0,), ((0, 4),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 4}, {0: 0}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 4), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 4), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 4), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 4))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 4), (0, 1), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 4), (0, 2), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 4), (0, 3), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 4))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 5))), + GriddedPerm((2, 1, 0), ((0, 4), (0, 4), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 4), (0, 4), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 4), (0, 4), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 4), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 4), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 4), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 3))), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 4), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 4), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 4), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 4), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 4), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 4), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 4), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 4), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 4), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 4), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 4), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 4), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 4), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 4), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 5), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 4), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 4), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 4), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 5), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 4), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 4), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 4), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 5), (0, 5), (0, 5), (0, 0)), + ), + ), + requirements=((GriddedPerm((0,), ((0, 3),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 4}, {0: 0}), + ), + ) + ), + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 2))), + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 1), (0, 4))), + GriddedPerm((0, 1), ((0, 2), (0, 4))), + GriddedPerm((0, 1), ((0, 3), (0, 4))), + GriddedPerm((1, 0), ((0, 2), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 4), (0, 1))), + GriddedPerm((1, 0), ((0, 4), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 4), (0, 5), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 4), (0, 5))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 4))), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 3), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 2), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 2), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 3), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 3), (0, 2), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 2), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 2), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 3), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 3), (0, 3), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 3), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 3), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 5), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 3), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 5), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 3), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 5), (0, 5), (0, 5), (0, 0)), + ), + ), + requirements=((GriddedPerm((0,), ((0, 4),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 2, 3: 2, 4: 3, 5: 4}, {0: 0}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 1), (0, 4))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 4))), + GriddedPerm((0, 1), ((0, 3), (0, 4))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 1))), + GriddedPerm((1, 0), ((0, 4), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 4), (0, 5), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 4), (0, 5))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 4))), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 1), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 1), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 2), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 2), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 2), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 2), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 2), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 5), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 2), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 5), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 2), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 5), (0, 5), (0, 5), (0, 0)), + ), + ), + requirements=((GriddedPerm((0,), ((0, 4),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 4}, {0: 0}), + ), + ) ), - TrackingAssumption( - (GriddedPerm((0,), ((0, 3),)), GriddedPerm((0,), ((0, 4),))) + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 3))), + GriddedPerm((0, 1), ((0, 1), (0, 4))), + GriddedPerm((0, 1), ((0, 2), (0, 3))), + GriddedPerm((0, 1), ((0, 2), (0, 4))), + GriddedPerm((0, 1), ((0, 3), (0, 4))), + GriddedPerm((1, 0), ((0, 3), (0, 1))), + GriddedPerm((1, 0), ((0, 3), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 1))), + GriddedPerm((1, 0), ((0, 4), (0, 2))), + GriddedPerm((1, 0), ((0, 4), (0, 3))), + GriddedPerm((0, 2, 1), ((0, 1), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 2), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 3), (0, 5), (0, 5))), + GriddedPerm((0, 2, 1), ((0, 4), (0, 5), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 1), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 2), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 3), (0, 5))), + GriddedPerm((2, 0, 1), ((0, 5), (0, 4), (0, 5))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 1))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 2))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 3))), + GriddedPerm((2, 1, 0), ((0, 5), (0, 5), (0, 4))), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 1)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 1), (0, 1), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 1), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 1), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 2), (0, 2), (0, 5), (0, 2)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 3), (0, 3), (0, 5), (0, 3)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 4), (0, 4), (0, 5), (0, 4)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 2), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 2), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 1), (0, 5), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 1), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 0, 2), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 1), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 2), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 2), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 1), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 1), (0, 5), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 2), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 1)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 2), (0, 5), (0, 2), (0, 2)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 3), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 3), (0, 5), (0, 3), (0, 3)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 4), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 4), (0, 5), (0, 4), (0, 4)) + ), + GriddedPerm( + (1, 3, 2, 0), ((0, 5), (0, 5), (0, 5), (0, 5)) + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 0), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 1), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 2), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 1)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 1), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 2), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 2), (0, 0), (0, 5), (0, 2)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 3), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 3), (0, 0), (0, 5), (0, 3)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 4), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 4), (0, 0), (0, 5), (0, 4)), + ), + GriddedPerm( + (0, 2, 1, 4, 3), + ((0, 0), (0, 5), (0, 0), (0, 5), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 1), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 2), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 1)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 1), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 2), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 2), (0, 5), (0, 0), (0, 2)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 3), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 3), (0, 5), (0, 0), (0, 3)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 4), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 4), (0, 5), (0, 0), (0, 4)), + ), + GriddedPerm( + (0, 2, 4, 1, 3), + ((0, 0), (0, 5), (0, 5), (0, 0), (0, 5)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 0), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 0), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 0), (0, 5), (0, 5), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 1), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 2), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 1), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 1), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 2), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 2), (0, 5), (0, 2), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 3), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 3), (0, 5), (0, 3), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 4), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 4), (0, 5), (0, 4), (0, 0)), + ), + GriddedPerm( + (0, 2, 4, 3, 1), + ((0, 0), (0, 5), (0, 5), (0, 5), (0, 0)), + ), + ), + requirements=((GriddedPerm((0,), ((0, 4),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 4}, {0: 0}), + ), + ) ), ), ) + rule = EquivalencePathRule([strategy(tiling).to_reverse_rule(0)]) for i in range(5): rule.sanity_check(i) diff --git a/tox.ini b/tox.ini index d188053c..1fd89653 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py --ignore=tests/strategies/test_sliding.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From 66e217e57bcd68b2610fb7c086db183e52c4e0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 18 Nov 2021 15:39:38 +0000 Subject: [PATCH 26/41] Changin' fuse criteria (#389) * not account for extra when deciding side of param * some test for the new fusion * jays alt fusion, left/right param determined only by preimage of maps * row -> col * clean jay fusion criteria * sort import * pylint Co-authored-by: Christian Bean --- tests/test_parameter_rule.py | 90 ++++++++++++++++++++++++++++++++++++ tilings/algorithms/fusion.py | 65 ++++++++++++++++++++------ tilings/parameter_counter.py | 10 ++-- 3 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 tests/test_parameter_rule.py diff --git a/tests/test_parameter_rule.py b/tests/test_parameter_rule.py new file mode 100644 index 00000000..2bf6aa15 --- /dev/null +++ b/tests/test_parameter_rule.py @@ -0,0 +1,90 @@ +import pytest + +from comb_spec_searcher.exception import StrategyDoesNotApply +from tilings import GriddedPerm, Tiling +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter +from tilings.strategies.fusion import FusionStrategy + + +def test_counting_fusion_rule(): + t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + ), + requirements=(), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 1, 2: 1}), + ), + ) + ), + ), + ) + strategy = FusionStrategy(col_idx=0, tracked=True) + rule = strategy(t) + for n in range(5): + rule._sanity_check_count(n) + + +def test_fusion(): + t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + ), + requirements=(), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1), ((0, 0), (1, 0))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 0), (2, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0, 2: 1}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 0), (1, 0))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0}, {0: 1, 1: 1}), + ), + ) + ), + ), + ) + strategy = FusionStrategy(col_idx=0, tracked=True) + with pytest.raises(StrategyDoesNotApply): + strategy(t) diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index aaefba33..b1c91476 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -1,8 +1,9 @@ """ The implementation of the fusion algorithm """ +import functools import itertools -from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple +from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional, Tuple from tilings.griddedperm import GriddedPerm from tilings.map import RowColMap @@ -20,8 +21,8 @@ class Fusion: - MAX_NUMBER_EXTRA = 0 - MAX_LENGTH_EXTRA = 0 + MAX_NUMBER_EXTRA = 2 + MAX_LENGTH_EXTRA = 2 MAX_NUM_PARAMS = 1 def __init__( @@ -86,10 +87,9 @@ def is_fusable_param(self, parameter_counter: ParameterCounter) -> bool: for preimage in parameter_counter.counters ) - def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: - row1, row2 = self.get_preimage_fuse_indices(preimage) - if row1 is not None and row2 is not None and row1 + 1 != row2: - return False + def _active_region_of_preimage_intersects_fuse_region( + self, preimage: PreimageCounter + ) -> bool: if self._fuse_row: fuse_region = self.tiling.cells_in_row(self._row_idx).union( self.tiling.cells_in_row(self._row_idx + 1) @@ -98,9 +98,46 @@ def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: fuse_region = self.tiling.cells_in_col(self._col_idx).union( self.tiling.cells_in_col(self._col_idx + 1) ) - if preimage.active_region(self.tiling).intersection(fuse_region): - return self.fused_preimage(preimage) == self.new_parameter().counters[0] - return True + return bool(preimage.active_region(self.tiling).intersection(fuse_region)) + + def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: + if not self._active_region_of_preimage_intersects_fuse_region(preimage): + return True + return preimage.tiling == self.allowed_preimage + + @functools.cached_property + def allowed_preimage(self) -> "Tiling": + obs: List[GriddedPerm] = [] + reqs: List[List[GriddedPerm]] = [] + unfuse_map = list(self._unfuse_maps()) + for rowcolmap in unfuse_map: + obs.extend(rowcolmap.preimage_gps(self.tiling.obstructions)) + for req in self.tiling.requirements: + new_req = [] + for rowcolmap in unfuse_map: + new_req.extend(list(rowcolmap.preimage_gps(req))) + reqs.append(new_req) + return self.tiling.__class__(obs, reqs) + + def _unfuse_maps(self) -> Iterator[RowColMap]: + if self._fuse_row: + num_col, num_row = self.tiling.dimensions + col_map = {i: i for i in range(num_col)} + num_row += 1 + for row in (self._row_idx, self._row_idx + 1): + row_map = {i: i for i in range(num_row)} + for i in range(row + 1, num_row): + row_map[i] = i - 1 + yield RowColMap(row_map, col_map) + else: + num_col, num_row = self.tiling.dimensions + row_map = {i: i for i in range(num_row)} + num_col += 1 + for col in (self._col_idx, self._col_idx + 1): + col_map = {i: i for i in range(num_col)} + for i in range(col + 1, num_col): + col_map[i] = i - 1 + yield RowColMap(row_map, col_map) def get_preimage_fuse_indices( self, preimage: PreimageCounter @@ -247,13 +284,13 @@ def is_left_sided_parameter(self, parameter: ParameterCounter) -> bool: return all( y != self._row_idx + 1 for _, y in itertools.chain.from_iterable( - parameter.active_regions(self.tiling) + parameter.active_regions(self.tiling, True) ) ) return all( x != self._col_idx + 1 for x, _ in itertools.chain.from_iterable( - parameter.active_regions(self.tiling) + parameter.active_regions(self.tiling, True) ) ) @@ -265,13 +302,13 @@ def is_right_sided_parameter(self, parameter: ParameterCounter) -> bool: return all( y != self._row_idx for _, y in itertools.chain.from_iterable( - parameter.active_regions(self.tiling) + parameter.active_regions(self.tiling, True) ) ) return all( x != self._col_idx for x, _ in itertools.chain.from_iterable( - parameter.active_regions(self.tiling) + parameter.active_regions(self.tiling, True) ) ) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 2c79183f..27b62458 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -58,7 +58,7 @@ def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": self.map = self.map.compose(row_col_map) return self - def active_region(self, tiling: "Tiling") -> Set[Cell]: + def active_region(self, tiling: "Tiling", ignore_extra: bool = False) -> Set[Cell]: """ Yield the active region of the preimage counter. @@ -68,6 +68,8 @@ def active_region(self, tiling: "Tiling") -> Set[Cell]: for cell in self.tiling.active_cells: if sum(1 for _ in self.map.preimage_cell(cell)) > 1: res.add(cell) + if ignore_extra: + return res extra_obs, extra_reqs = self.extra_obs_and_reqs(tiling) for gp in itertools.chain(extra_obs, *extra_reqs): res.update(gp.pos) @@ -167,14 +169,16 @@ def __init__(self, counters: Iterable[PreimageCounter]): sorted(itertools.filterfalse(PreimageCounter.is_empty, counters)) ) - def active_regions(self, tiling: "Tiling") -> Iterator[Set[Cell]]: + def active_regions( + self, tiling: "Tiling", ignore_extra: bool = False + ) -> Iterator[Set[Cell]]: """ Yield the active regions of the preimage counters. The cell are on the underlying tiling. """ for preimage in self: - yield preimage.active_region(tiling) + yield preimage.active_region(tiling, ignore_extra) def sub_param( self, cells: Set[Cell], underlying_tiling: "Tiling" From 57b0a5329c048c437ac6a44d3c1d738104adcadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Thu, 18 Nov 2021 16:25:37 +0000 Subject: [PATCH 27/41] Positive fusion and fusion out of bound (#391) * handle positive fusion with new param * prevent fusion when out of bound * isort --- tests/test_parameter_rule.py | 36 +++++++++++++++++++++++- tilings/algorithms/fusion.py | 5 ++++ tilings/strategies/fusion/constructor.py | 18 ++---------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/tests/test_parameter_rule.py b/tests/test_parameter_rule.py index 2bf6aa15..59e280ca 100644 --- a/tests/test_parameter_rule.py +++ b/tests/test_parameter_rule.py @@ -4,7 +4,7 @@ from tilings import GriddedPerm, Tiling from tilings.map import RowColMap from tilings.parameter_counter import ParameterCounter, PreimageCounter -from tilings.strategies.fusion import FusionStrategy +from tilings.strategies.fusion import FusionFactory, FusionStrategy def test_counting_fusion_rule(): @@ -88,3 +88,37 @@ def test_fusion(): strategy = FusionStrategy(col_idx=0, tracked=True) with pytest.raises(StrategyDoesNotApply): strategy(t) + + +def test_positive_fusion(): + t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + ), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=(), + ) + strategy = FusionStrategy(row_idx=0, tracked=True) + rule = strategy(t) + for n in range(5): + rule._sanity_check_count(n) + + +def test_col_fuse_with_empty(): + t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 1), (0, 1))), + GriddedPerm((0, 1, 2), ((0, 1), (0, 1), (0, 1))), + ), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=(), + ) + strategy = FusionStrategy(col_idx=1, tracked=True) + with pytest.raises(StrategyDoesNotApply): + strategy(t) + + factory = FusionFactory() + assert sum(1 for _ in factory(t)) == 1 diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index b1c91476..0a672cca 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -215,6 +215,11 @@ def fusable(self) -> bool: """ Return True if tiling is fusable. """ + if (self._fuse_row and self._row_idx > self.tiling.dimensions[1] - 2) or ( + not self._fuse_row and self._col_idx > self.tiling.dimensions[0] - 2 + ): + # Cannot fuse if the row or column index is too big. + return False if any( not self.is_fusable_param(parameter) for parameter in self.tiling.parameters ): diff --git a/tilings/strategies/fusion/constructor.py b/tilings/strategies/fusion/constructor.py index b04dff2e..f85b6f7c 100644 --- a/tilings/strategies/fusion/constructor.py +++ b/tilings/strategies/fusion/constructor.py @@ -254,19 +254,6 @@ def get_terms( the terms of size `n`. """ new_terms: Terms = Counter() - - min_left, min_right = self.min_points - - def add_new_term( - params: List[int], value: int, left_points: int, fuse_region_griddings: int - ) -> None: - """Update new terms if there is enough points on the left and right.""" - if ( - min_left <= fuse_region_griddings - 1 - and min_right <= fuse_region_griddings - left_points + 1 - ): # <- NEW: should be a -1, since 1 gridding implies 0 points in our logic - new_terms[tuple(params)] += value - for param, value in subterms[0](n).items(): fuse_region_griddings = param[self.fuse_parameter_index] new_params = list(self.children_param_map(param)) @@ -276,13 +263,12 @@ def add_new_term( new_params[idx] += 1 for idx in self.both_parameter_indices: new_params[idx] += 1 - for left_points in range(1, fuse_region_griddings + 1): + for _ in range(1, fuse_region_griddings + 1): for idx in self.left_parameter_indices: new_params[idx] += 1 for idx in self.right_parameter_indices: new_params[idx] -= 1 - - add_new_term(new_params, value, left_points, fuse_region_griddings) + new_terms[tuple(new_params)] += value return new_terms def determine_number_of_points_in_fuse_region( From 6b25a99d96bca6037003cf7eccdad4a2f0b7d39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Fri, 19 Nov 2021 13:13:45 +0000 Subject: [PATCH 28/41] Rectrictin the removal of preimage req strategy (#392) * Change the remove req factory Only return rules where there is one identity preimage to remove on the child. * test that we get hennings weird 123 tree * isort * add conditions needed to find component tree verification * Revert "add conditions needed to find component tree verification" This reverts commit 2b07fb8073d438d14f06e5282bdfaf9f4093d7c9. * making the test more consistent * remove unused import Co-authored-by: Christian Bean --- tests/test_assumptions.py | 40 ++++++++++++++++ tests/test_parameter_rule.py | 55 ++++++++++++++++++++++ tilings/strategies/parameter_strategies.py | 21 +++++---- 3 files changed, 108 insertions(+), 8 deletions(-) diff --git a/tests/test_assumptions.py b/tests/test_assumptions.py index 025f8998..63277c5b 100644 --- a/tests/test_assumptions.py +++ b/tests/test_assumptions.py @@ -198,6 +198,46 @@ def test_123_fusion(): ] +@pytest.mark.timeout(90) +def test_123_ppfusion(): + pack = TileScopePack.point_placements().make_fusion(tracked=True) + strat = pack.expansion_strats[0][1] + assert strat.__class__.__name__ == "PatternPlacementFactory" + strat.dirs = (0, 3) + pack.initial_strats + pack.ver_strats = pack.ver_strats[:1] + css = TileScope("123", pack) + spec = css.auto_search(status_update=30) + assert isinstance(spec, CombinatorialSpecification) + spec = spec.expand_verified() + assert [spec.count_objects_of_size(i) for i in range(20)] == [ + 1, + 1, + 2, + 5, + 14, + 42, + 132, + 429, + 1430, + 4862, + 16796, + 58786, + 208012, + 742900, + 2674440, + 9694845, + 35357670, + 129644790, + 477638700, + 1767263190, + ] + assert any( + "fuse" in rule.formal_step and rule.comb_class.dimensions == (2, 2) + for rule in spec + ) + + @pytest.mark.xfail @pytest.mark.timeout(90) def test_123_fusion_generate_and_sample(): diff --git a/tests/test_parameter_rule.py b/tests/test_parameter_rule.py index 59e280ca..cdd1676b 100644 --- a/tests/test_parameter_rule.py +++ b/tests/test_parameter_rule.py @@ -5,6 +5,7 @@ from tilings.map import RowColMap from tilings.parameter_counter import ParameterCounter, PreimageCounter from tilings.strategies.fusion import FusionFactory, FusionStrategy +from tilings.strategies.parameter_strategies import RemoveReqFactory def test_counting_fusion_rule(): @@ -122,3 +123,57 @@ def test_col_fuse_with_empty(): factory = FusionFactory() assert sum(1 for _ in factory(t)) == 1 + + +def test_remove_req_identity(): + """ + Make sure that the remove req factory only return rule were there's actually an + identity to remove. + """ + t = Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1, 2), ((0, 1), (2, 1), (2, 1))), + GriddedPerm((0, 1, 2), ((1, 0), (2, 1), (2, 1))), + GriddedPerm((0, 1, 2), ((2, 0), (2, 1), (2, 1))), + GriddedPerm((0, 1, 2), ((2, 1), (2, 1), (2, 1))), + ), + requirements=(), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 2), (0, 2))), + GriddedPerm((0, 1), ((1, 1), (1, 1))), + GriddedPerm((0, 1), ((2, 0), (2, 0))), + GriddedPerm((0, 1), ((2, 2), (2, 2))), + ), + requirements=((GriddedPerm((0,), ((1, 1),)),),), + parameters=(), + ), + RowColMap({0: 0, 1: 0, 2: 1}, {0: 0, 1: 1, 2: 2}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((0, 1), (0, 1))), + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 1), (1, 1), (1, 1))), + GriddedPerm((0, 1, 2), ((1, 0), (1, 1), (1, 1))), + GriddedPerm((0, 1, 2), ((1, 1), (1, 1), (1, 1))), + ), + requirements=(), + parameters=(), + ), + RowColMap({0: 0, 1: 1}, {0: 0, 1: 2}), + ), + ) + ), + ), + ) + with pytest.raises(StopIteration): + next(RemoveReqFactory()(t)) diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 61105306..09df1fdb 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -548,14 +548,19 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]: new_param ) param_strategy = RequirementInsertionStrategy(req) - strategy = DisjointParameterStrategy( - param_strategy, - new_comb_class.parameters.index(new_param), - new_param.counters.index(new_preimage), - False, - ) - rule = strategy(new_comb_class) - yield rule + if any( + child == comb_class.remove_parameters() + and PreimageCounter(child, preimg.map).map.is_identity() + for child in param_strategy(new_tiling).children + ): + strategy = DisjointParameterStrategy( + param_strategy, + new_comb_class.parameters.index(new_param), + new_param.counters.index(new_preimage), + False, + ) + rule = strategy(new_comb_class) + yield rule def __str__(self) -> str: return "Remove requirements from preimages" From 2eb33cbe4a53a7dd0bc866ed9df7c634cf952977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Fri, 19 Nov 2021 15:50:21 +0000 Subject: [PATCH 29/41] sort input when adding a parameters --- tilings/tiling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tilings/tiling.py b/tilings/tiling.py index 05c8467a..96d9beb8 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -620,7 +620,7 @@ def add_parameters(self, parameters: Iterable[ParameterCounter]) -> "Tiling": remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, - sorted_input=True, + sorted_input=False, ) return tiling From 967a30344d40603a63e8ddcbe91f980a951db7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Fri, 19 Nov 2021 17:27:56 +0000 Subject: [PATCH 30/41] more fine grain control on fusion and relaxing fuse condition When we compare the tiling with its unfused fused version we do not look at parameters --- tilings/algorithms/fusion.py | 62 +++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 0a672cca..a4018d02 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -22,8 +22,14 @@ class Fusion: MAX_NUMBER_EXTRA = 2 + MAX_NUMBER_EXTRA_LOCAL = 2 + MAX_NUMBER_EXTRA_CROSSING = 2 + MAX_NUMBER_EXTRA_LEAVING = 2 MAX_LENGTH_EXTRA = 2 - MAX_NUM_PARAMS = 1 + MAX_LENGTH_EXTRA_LOCAL = 2 + MAX_LENGTH_EXTRA_CROSSING = 2 + MAX_LENGTH_EXTRA_LEAVING = 2 + MAX_NUM_PARAMS = 2 def __init__( self, @@ -225,17 +231,49 @@ def fusable(self) -> bool: ): return False obs, reqs, _ = self.unfused_fused_obs_reqs_and_params() - if self.tiling == self.tiling.add_obstructions_and_requirements(obs, reqs): - ft = self.fused_tiling() - if len(ft.parameters) <= self.MAX_NUM_PARAMS: - eobs, ereqs = self.extra_obs_and_reqs() - if ( - not ereqs - and len(eobs) <= self.MAX_NUMBER_EXTRA - and all(len(gp) <= self.MAX_LENGTH_EXTRA for gp in eobs) - ): - return True - return False + unfused_fused_tiling = ( + self.tiling.remove_parameters().add_obstructions_and_requirements(obs, reqs) + ) + return ( + self.tiling.remove_parameters() == unfused_fused_tiling + and self._check_fusion_restriction() + ) + + def _check_fusion_restriction(self) -> bool: + ft = self.fused_tiling() + if len(ft.parameters) > self.MAX_NUM_PARAMS: + return False + eobs, ereqs = self.extra_obs_and_reqs() + eobs_local = frozenset(filter(GriddedPerm.is_localized, eobs)) + eobs_crossing = frozenset(filter(self._is_crossing, eobs)) + eobs_leaving = frozenset( + gp for gp in eobs if gp not in eobs_local and gp not in eobs_crossing + ) + return ( + not ereqs + and len(eobs) <= self.MAX_NUMBER_EXTRA + and len(eobs_local) <= self.MAX_NUMBER_EXTRA_LOCAL + and len(eobs_crossing) <= self.MAX_NUMBER_EXTRA_CROSSING + and len(eobs_leaving) <= self.MAX_NUMBER_EXTRA_LEAVING + and max(map(len, eobs), default=0) <= self.MAX_LENGTH_EXTRA + and max(map(len, eobs_local), default=0) <= self.MAX_LENGTH_EXTRA_LOCAL + and max(map(len, eobs_crossing), default=0) + <= self.MAX_LENGTH_EXTRA_CROSSING + and max(map(len, eobs_leaving), default=0) <= self.MAX_LENGTH_EXTRA_LEAVING + ) + + def _is_crossing(self, gp: GriddedPerm) -> bool: + """ + Check if the gridded permutation is not localized but stays only in the fuse + region. + """ + if self._fuse_row: + rows = (cell[1] for cell in gp.pos) + good_rows = (self._row_idx, self._row_idx + 1) + else: + rows = (cell[0] for cell in gp.pos) + good_rows = (self._col_idx, self._col_idx + 1) + return not gp.is_localized() and all(r in good_rows for r in rows) def fused_tiling(self) -> "Tiling": """ From 7007d4a27952a6cb035ce98325ea45918f544ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Fri, 19 Nov 2021 17:32:20 +0000 Subject: [PATCH 31/41] add always_count_one for preimage --- tilings/parameter_counter.py | 11 +++++++++++ tilings/strategies/parameter_strategies.py | 8 ++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tilings/parameter_counter.py b/tilings/parameter_counter.py index 27b62458..3bd6e582 100644 --- a/tilings/parameter_counter.py +++ b/tilings/parameter_counter.py @@ -58,6 +58,17 @@ def apply_row_col_map(self, row_col_map: "RowColMap") -> "PreimageCounter": self.map = self.map.compose(row_col_map) return self + def always_counts_one(self, underlying: "Tiling") -> bool: + """ + Returns True if the number of preimage of a gridded perm on underlying + is always 1. + """ + return ( + self.map.is_identity() + and self.tiling.obstructions == underlying.obstructions + and self.tiling.requirements == underlying.requirements + ) + def active_region(self, tiling: "Tiling", ignore_extra: bool = False) -> Set[Cell]: """ Yield the active region of the preimage counter. diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 09df1fdb..9929a847 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -227,10 +227,7 @@ def _map_param(comb_class: Tiling, param: ParameterCounter) -> ParameterCounter: preimgs = ( preimg for preimg in param.counters - if not ( - preimg.map.is_identity() - and preimg.tiling == comb_class.remove_parameters() - ) + if not preimg.always_counts_one(comb_class) ) return ParameterCounter(preimgs) @@ -549,8 +546,7 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]: ) param_strategy = RequirementInsertionStrategy(req) if any( - child == comb_class.remove_parameters() - and PreimageCounter(child, preimg.map).map.is_identity() + PreimageCounter(child, preimg.map).always_counts_one(comb_class) for child in param_strategy(new_tiling).children ): strategy = DisjointParameterStrategy( From 3042667bb022a05e03d645580f5b4e21a5bc3e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Nadeau?= Date: Fri, 19 Nov 2021 18:05:44 +0000 Subject: [PATCH 32/41] smarter remove req factory We now also try to remove requirement which maps to a requirement on the parent but are not the full preimage of the requirement --- tests/test_parameter_rule.py | 61 ++++++++++++++++++++++ tilings/strategies/parameter_strategies.py | 21 +++++--- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/tests/test_parameter_rule.py b/tests/test_parameter_rule.py index cdd1676b..9233c516 100644 --- a/tests/test_parameter_rule.py +++ b/tests/test_parameter_rule.py @@ -177,3 +177,64 @@ def test_remove_req_identity(): ) with pytest.raises(StopIteration): next(RemoveReqFactory()(t)) + + +def test_remove_req_factory(): + t = Tiling( + obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))),), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + ), + requirements=((GriddedPerm((0,), ((1, 0),)),),), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0}), + ), + ) + ), + ), + ) + strat = RemoveReqFactory() + assert sum(1 for _ in strat(t)) == 1 + assert next(strat(t)).children == ( + Tiling( + obstructions=(GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))),), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1), ((1, 0), (1, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (1, 0))), + ), + requirements=((GriddedPerm((0,), ((1, 0),)),),), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0}), + ), + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((0, 1, 2), ((0, 0), (0, 0), (0, 0))), + ), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=(), + ), + RowColMap({0: 0}, {0: 0}), + ), + ) + ), + ), + ), + ) diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 9929a847..75469760 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -525,15 +525,24 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]: for param_idx, param in enumerate(comb_class.parameters): for preimg_idx, preimg in enumerate(param): for req_idx, req in enumerate(preimg.tiling.requirements): + image_req = tuple(sorted(preimg.map.map_gps(req))) + preimage_image_req = tuple( + sorted(preimg.map.preimage_gps(image_req)) + ) if ( - tuple(sorted((preimg.map.map_gps(req)))) - in comb_class.requirements + image_req in comb_class.requirements + and preimage_image_req == req ): continue new_tiling = Tiling( preimg.tiling.obstructions, preimg.tiling.requirements[:req_idx] - + preimg.tiling.requirements[req_idx + 1 :], + + preimg.tiling.requirements[req_idx + 1 :] + + ( + (preimage_image_req,) + if preimage_image_req != req + else tuple() + ), ) new_preimage = PreimageCounter(new_tiling, preimg.map) new_param = ParameterCounter( @@ -549,14 +558,12 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]: PreimageCounter(child, preimg.map).always_counts_one(comb_class) for child in param_strategy(new_tiling).children ): - strategy = DisjointParameterStrategy( + yield DisjointParameterStrategy( param_strategy, new_comb_class.parameters.index(new_param), new_param.counters.index(new_preimage), False, - ) - rule = strategy(new_comb_class) - yield rule + )(new_comb_class) def __str__(self) -> str: return "Remove requirements from preimages" From 11482da1924098c557466b93b5d838f4ff038c68 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Tue, 23 Nov 2021 12:40:38 +0000 Subject: [PATCH 33/41] Add parameter strategy (#395) * fix the add parameter strategy * update init * assumption_insertion -> parameter_insertion * add mypy back in * fixing test import --- mypy.ini | 3 - tests/test_bijections.py | 2 +- tilings/strategies/__init__.py | 18 +++- tilings/strategies/factor.py | 18 ++-- ...on_insertion.py => parameter_insertion.py} | 89 +++++++++---------- tilings/strategy_pack.py | 30 +++---- tilings/tilescope.py | 42 +++++---- 7 files changed, 102 insertions(+), 100 deletions(-) rename tilings/strategies/{assumption_insertion.py => parameter_insertion.py} (77%) diff --git a/mypy.ini b/mypy.ini index 5a0d9caa..7c536d82 100644 --- a/mypy.ini +++ b/mypy.ini @@ -18,9 +18,6 @@ ignore_errors = True [mypy-tilings.bijections] ignore_errors = True -[mypy-tilings.strategies.assumption_insertion] -ignore_errors = True - [mypy-tilings.strategies.symmetry] ignore_errors = True diff --git a/tests/test_bijections.py b/tests/test_bijections.py index 567da555..3293e801 100644 --- a/tests/test_bijections.py +++ b/tests/test_bijections.py @@ -21,9 +21,9 @@ _AssumptionPathTracker, ) from tilings.strategies import BasicVerificationStrategy -from tilings.strategies.assumption_insertion import AddAssumptionsStrategy from tilings.strategies.factor import FactorStrategy from tilings.strategies.fusion import FusionStrategy +from tilings.strategies.parameter_insertion import AddParametersStrategy from tilings.strategies.requirement_placement import RequirementPlacementStrategy from tilings.strategies.sliding import SlidingFactory, SlidingStrategy from tilings.tilescope import TileScope, TileScopePack diff --git a/tilings/strategies/__init__.py b/tilings/strategies/__init__.py index 171f3a85..5b7076df 100644 --- a/tilings/strategies/__init__.py +++ b/tilings/strategies/__init__.py @@ -1,4 +1,3 @@ -from .assumption_insertion import AddAssumptionFactory, AddInterleavingAssumptionFactory from .assumption_splitting import SplittingStrategy from .detect_components import DetectComponentsStrategy from .experimental_verification import ( @@ -13,6 +12,13 @@ ObstructionTransitivityFactory, SubobstructionInferralFactory, ) +from .parameter_insertion import AddInterleavingParameterFactory, AddParameterFactory +from .parameter_strategies import ( + DisjointUnionParameterFactory, + ParameterVerificationStrategy, + RemoveIdentityPreimageStrategy, + RemoveReqFactory, +) from .rearrange_assumption import RearrangeAssumptionFactory from .requirement_insertion import ( CellInsertionFactory, @@ -43,11 +49,15 @@ ) __all__ = [ - # Assumptions - "AddAssumptionFactory", - "AddInterleavingAssumptionFactory", + # Parameters + "AddInterleavingParameterFactory", + "AddParameterFactory", "DetectComponentsStrategy", + "DisjointUnionParameterFactory", + "ParameterVerificationStrategy", "RearrangeAssumptionFactory", + "RemoveIdentityPreimageStrategy", + "RemoveReqFactory", "SplittingStrategy", # Batch "CellInsertionFactory", diff --git a/tilings/strategies/factor.py b/tilings/strategies/factor.py index 6ad1e562..929627e0 100644 --- a/tilings/strategies/factor.py +++ b/tilings/strategies/factor.py @@ -27,9 +27,9 @@ FactorWithInterleaving, FactorWithMonotoneInterleaving, ) -from tilings.assumptions import TrackingAssumption from tilings.exception import InvalidOperationError from tilings.misc import multinomial, partitions_iterator +from tilings.parameter_counter import ParameterCounter Cell = Tuple[int, int] @@ -154,7 +154,7 @@ def from_dict(cls, d: dict) -> "FactorStrategy": return cls(partition=partition, **d) -# The following functions are used to determine assumptions needed to count the +# The following functions are used to determine parameters needed to count the # interleavings of a factor. They are also used by AddInterleavingAssumptionStrategy. @@ -177,22 +177,22 @@ def interleaving_rows_and_cols( return cols, rows -def assumptions_to_add( +def parameters_to_add( cells: Tuple[Cell, ...], cols: Set[int], rows: Set[int] -) -> Tuple[TrackingAssumption, ...]: +) -> Tuple[ParameterCounter, ...]: """ - Return the assumptions that should be tracked in the set of cells if we are + Return the parameters that should be tracked in the set of cells if we are interleaving the given rows and cols. """ raise NotImplementedError("Don't know what to do with the preimage") -def contains_interleaving_assumptions( +def contains_interleaving_parameters( comb_class: Tiling, partition: Tuple[Tuple[Cell, ...], ...] ) -> bool: """ Return True if the parent tiling contains all of the necessary tracking - assumptions needed to count the interleavings, and therefore the + parameters needed to count the interleavings, and therefore the children too. """ raise NotImplementedError("Don't know what to do with the preimage") @@ -378,11 +378,11 @@ def __call__(self, comb_class: Tiling) -> Iterator[FactorStrategy]: components = tuple( tuple(chain.from_iterable(part)) for part in partition ) - if not self.tracked or contains_interleaving_assumptions( + if not self.tracked or contains_interleaving_parameters( comb_class, components ): yield self._build_strategy(components, workable=False) - if not self.tracked or contains_interleaving_assumptions( + if not self.tracked or contains_interleaving_parameters( comb_class, min_comp ): yield self._build_strategy(min_comp, workable=self.workable) diff --git a/tilings/strategies/assumption_insertion.py b/tilings/strategies/parameter_insertion.py similarity index 77% rename from tilings/strategies/assumption_insertion.py rename to tilings/strategies/parameter_insertion.py index 409fd06e..da4e580f 100644 --- a/tilings/strategies/assumption_insertion.py +++ b/tilings/strategies/parameter_insertion.py @@ -20,15 +20,15 @@ ) from tilings import GriddedPerm, Tiling from tilings.algorithms import FactorWithInterleaving -from tilings.assumptions import TrackingAssumption from tilings.misc import partitions_iterator +from tilings.parameter_counter import ParameterCounter -from .factor import assumptions_to_add, interleaving_rows_and_cols +from .factor import interleaving_rows_and_cols, parameters_to_add Cell = Tuple[int, int] -class AddAssumptionsConstructor(Constructor): +class AddParametersConstructor(Constructor): """ The constructor used to count when a new variable is added. """ @@ -62,10 +62,10 @@ def get_terms( self, parent_terms: Callable[[int], Terms], subterms: SubTerms, n: int ) -> Terms: assert len(subterms) == 1 - return self._push_add_assumption(n, subterms[0], self._child_param_map) + return self._push_add_parameter(n, subterms[0], self._child_param_map) @staticmethod - def _push_add_assumption( + def _push_add_parameter( n: int, child_terms: Callable[[int], Terms], child_param_map: ParametersMap, @@ -122,16 +122,16 @@ def equiv( return ( isinstance(other, type(self)) and len(other.new_parameters) == len(self.new_parameters) - and AddAssumptionsConstructor.extra_params_equiv( + and AddParametersConstructor.extra_params_equiv( (self.extra_parameters,), (other.extra_parameters,) ), None, ) -class AddAssumptionsStrategy(Strategy[Tiling, GriddedPerm]): - def __init__(self, assumptions: Iterable[TrackingAssumption], workable=False): - self.assumptions = tuple(set(assumptions)) +class AddParametersStrategy(Strategy[Tiling, GriddedPerm]): + def __init__(self, parameters: Iterable[ParameterCounter], workable=False): + self.parameters = tuple(set(parameters)) super().__init__( ignore_parent=False, inferrable=True, @@ -158,21 +158,21 @@ def shifts( return (0,) def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: - if any(assumption in comb_class.assumptions for assumption in self.assumptions): - raise StrategyDoesNotApply("The assumption is already on the tiling.") - return (comb_class.add_assumptions(self.assumptions),) + if any(parameter in comb_class.parameters for parameter in self.parameters): + raise StrategyDoesNotApply("The parameter is already on the tiling.") + return (comb_class.add_parameters(self.parameters),) def constructor( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None - ) -> AddAssumptionsConstructor: + ) -> AddParametersConstructor: if children is None: children = self.decomposition_function(comb_class) if children is None: - raise StrategyDoesNotApply("Can't split the tracking assumption") + raise StrategyDoesNotApply("Can't add the parameter") new_parameters = [ - children[0].get_assumption_parameter(ass) for ass in self.assumptions + children[0].get_parameter_name(param) for param in self.parameters ] - return AddAssumptionsConstructor( + return AddParametersConstructor( comb_class, children[0], new_parameters, @@ -197,18 +197,16 @@ def extra_parameters( child = children[0] return ( { - comb_class.get_assumption_parameter( - ass - ): child.get_assumption_parameter(ass) - for ass in comb_class.assumptions + comb_class.get_parameter_name(param): child.get_parameter_name(param) + for param in comb_class.parameters }, ) def formal_step(self) -> str: - if len(self.assumptions) == 1: - return f"adding the assumption '{self.assumptions[0]}'" - assumptions = ", ".join([f"'{ass}'" for ass in self.assumptions]) - return f"adding the assumptions '{assumptions}'" + if len(self.parameters) == 1: + return f"adding the parameter '{self.parameters[0]}'" + parameters = ", ".join([f"'{param}'" for param in self.parameters]) + return f"adding the parameters '{parameters}'" def backward_map( self, @@ -244,13 +242,12 @@ def to_jsonable(self) -> dict: d.pop("ignore_parent") d.pop("inferrable") d.pop("possibly_empty") - d["assumptions"] = [ass.to_jsonable() for ass in self.assumptions] + d["parameters"] = [param.to_jsonable() for param in self.parameters] return d @classmethod - def from_dict(cls, d: dict) -> "AddAssumptionsStrategy": - assumptions = [TrackingAssumption.from_dict(ass) for ass in d["assumptions"]] - return cls(assumptions) + def from_dict(cls, d: dict) -> "AddParametersStrategy": + raise NotImplementedError @staticmethod def get_eq_symbol() -> str: @@ -259,33 +256,33 @@ def get_eq_symbol() -> str: def __repr__(self): return ( self.__class__.__name__ - + f"(assumptions={repr(self.assumptions)}, workable={self.workable})" + + f"(parameters={repr(self.parameters)}, workable={self.workable})" ) -class AddAssumptionFactory(StrategyFactory[Tiling]): +class AddParameterFactory(StrategyFactory[Tiling]): def __call__(self, comb_class: Tiling) -> Iterator[Rule]: - for assumption in comb_class.assumptions: - without = comb_class.remove_assumption(assumption) - strategy = AddAssumptionsStrategy((assumption,)) + for parameter in comb_class.parameters: + without = comb_class.remove_parameter(parameter) + strategy = AddParametersStrategy((parameter,)) yield strategy(without) def __repr__(self) -> str: return self.__class__.__name__ + "()" def __str__(self) -> str: - return "add assumptions" + return "add parameters" def to_jsonable(self) -> dict: d: dict = super().to_jsonable() return d @classmethod - def from_dict(cls, d: dict) -> "AddAssumptionFactory": + def from_dict(cls, d: dict) -> "AddParameterFactory": return cls() -class AddInterleavingAssumptionFactory(StrategyFactory[Tiling]): +class AddInterleavingParameterFactory(StrategyFactory[Tiling]): def __init__(self, unions: bool = False): self.unions = unions @@ -294,18 +291,18 @@ def strategy_from_components( comb_class: Tiling, components: Tuple[Tuple[Cell, ...], ...] ) -> Iterator[Rule]: """ - Yield an AddAssumption strategy for the given component if needed. + Yield an AddParameter strategy for the given component if needed. """ cols, rows = interleaving_rows_and_cols(components) - assumptions = set( - ass - for ass in chain.from_iterable( - assumptions_to_add(cells, cols, rows) for cells in components + parameters = set( + param + for param in chain.from_iterable( + parameters_to_add(cells, cols, rows) for cells in components ) - if ass not in comb_class.assumptions + if param not in comb_class.parameters ) - if assumptions: - strategy = AddAssumptionsStrategy(assumptions, workable=True) + if parameters: + strategy = AddParametersStrategy(parameters, workable=True) yield strategy(comb_class) # TODO: monotone? @@ -325,7 +322,7 @@ def __repr__(self) -> str: return self.__class__.__name__ + "()" def __str__(self) -> str: - return "add interleaving assumptions to factor" + return "add interleaving parameters to factor" def to_jsonable(self) -> dict: d: dict = super().to_jsonable() @@ -333,5 +330,5 @@ def to_jsonable(self) -> dict: return d @classmethod - def from_dict(cls, d: dict) -> "AddInterleavingAssumptionFactory": + def from_dict(cls, d: dict) -> "AddInterleavingParameterFactory": return cls(**d) diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index cc6781ef..9643f250 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -12,12 +12,6 @@ from permuta import Perm from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS from tilings import strategies as strat -from tilings.strategies.parameter_strategies import ( - DisjointUnionParameterFactory, - ParameterVerificationStrategy, - RemoveIdentityPreimageStrategy, - RemoveReqFactory, -) from tilings.strategies.verification import BasisAwareVerificationStrategy if TYPE_CHECKING: @@ -169,12 +163,15 @@ def make_fusion( pack = self.add_initial(fusion_strat, name, apply_first=apply_first) if tracked: pack = pack.add_initial( - DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + strat.DisjointUnionParameterFactory(strat.FactorInsertionFactory()), apply_first=True, ) - pack = pack.add_initial(RemoveReqFactory(), apply_first=True) - pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) - pack = pack.add_verification(ParameterVerificationStrategy()) + pack = pack.add_initial(strat.RemoveReqFactory(), apply_first=True) + pack = pack.add_initial( + strat.RemoveIdentityPreimageStrategy(), apply_first=True + ) + pack = pack.add_initial(strat.AddParameterFactory(), apply_first=True) + pack = pack.add_verification(strat.ParameterVerificationStrategy()) return pack def make_interleaving( @@ -218,15 +215,18 @@ def replace_list(strats): if tracked: pack = pack.add_initial( - strat.AddInterleavingAssumptionFactory(unions=unions), apply_first=True + strat.AddInterleavingParameterFactory(unions=unions), apply_first=True ) pack = pack.add_initial( - DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + strat.DisjointUnionParameterFactory(strat.FactorInsertionFactory()), apply_first=True, ) - pack = pack.add_initial(RemoveReqFactory(), apply_first=True) - pack = pack.add_initial(RemoveIdentityPreimageStrategy(), apply_first=True) - pack = pack.add_verification(ParameterVerificationStrategy()) + pack = pack.add_initial(strat.RemoveReqFactory(), apply_first=True) + pack = pack.add_initial( + strat.RemoveIdentityPreimageStrategy(), apply_first=True + ) + pack = pack.add_initial(strat.AddParameterFactory(), apply_first=True) + pack = pack.add_verification(strat.ParameterVerificationStrategy()) return pack def make_elementary(self) -> "TileScopePack": diff --git a/tilings/tilescope.py b/tilings/tilescope.py index 6c959e3f..caf78063 100644 --- a/tilings/tilescope.py +++ b/tilings/tilescope.py @@ -16,12 +16,12 @@ from comb_spec_searcher.typing import CombinatorialClassType, CSSstrategy from permuta import Basis, Perm from tilings import GriddedPerm, Tiling -from tilings.strategies import AddAssumptionFactory, RearrangeAssumptionFactory -from tilings.strategies.assumption_insertion import AddAssumptionsStrategy +from tilings.strategies import AddParameterFactory, RearrangeAssumptionFactory +from tilings.strategies.parameter_insertion import AddParametersStrategy from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy from tilings.strategy_pack import TileScopePack -__all__ = ("TileScope", "TileScopePack", "LimitedAssumptionTileScope", "GuidedSearcher") +__all__ = ("TileScope", "TileScopePack", "LimitedParameterTileScope", "GuidedSearcher") class TileScope(CombinatorialSpecificationSearcher): @@ -73,21 +73,21 @@ def __init__( ) -class LimitedAssumptionTileScope(TileScope): +class LimitedParameterTileScope(TileScope): """ A subclass of Tilescope that allows a limit to be set on the maximum number of - assumptions that appear on any tiling in the universe. + parameters that appear on any tiling in the universe. """ def __init__( self, start_class: Union[str, Iterable[Perm], Tiling], strategy_pack: TileScopePack, - max_assumptions: int, + max_parameters: int, **kwargs, ) -> None: super().__init__(start_class, strategy_pack, **kwargs) - self.max_assumptions = max_assumptions + self.max_parameters = max_parameters def _expand( self, @@ -98,7 +98,7 @@ def _expand( ) -> None: """ Will expand the combinatorial class with given label using the given - strategies, but only add rules whose children all satisfy the max_assumptions + strategies, but only add rules whose children all satisfy the max_parameters requirement. """ if inferral: @@ -109,7 +109,7 @@ def _expand( comb_class, strategy_generator, label ): if all( - len(child.assumptions) <= self.max_assumptions + len(child.parameters) <= self.max_parameters for child in rule.children ): self.add_rule(start_label, end_labels, rule) @@ -159,17 +159,17 @@ def from_uri(cls, URI: str) -> "GuidedSearcher": return cls.from_spec(spec, pack) -class TrackedSearcher(LimitedAssumptionTileScope): +class TrackedSearcher(LimitedParameterTileScope): """ A TileScope that only adds underlying tilings to the queue, but expands all - assumption tilings with the strategies that apply to the underlying tiling + parameter tilings with the strategies that apply to the underlying tiling immediately. """ def __init__(self, *args, **kwargs): self.tilings_from_underlying: DefaultDict[int, Set[int]] = defaultdict(set) self.tracking_strategies = [ - AddAssumptionFactory(), + AddParameterFactory(), RearrangeAssumptionFactory(), ] self.tracked_expanded: Set[int] = set() @@ -193,18 +193,16 @@ def add_rule( - try to verify children combinatorial classes - set workability of combinatorial classes - symmetry expand combinatorial classes - - add underlying class to classqueue, and do the expansion for any assumption + - add underlying class to classqueue, and do the expansion for any parameter tilings with same underlying tiling """ for comb_class, child_label in zip(rule.children, end_labels): underlying_tiling = ( - comb_class.remove_assumptions() - if comb_class.assumptions - else comb_class + comb_class.remove_parameters() if comb_class.parameters else comb_class ) underlying_label = ( self.classdb.get_label(underlying_tiling) - if comb_class.assumptions + if comb_class.parameters else child_label ) if underlying_label != child_label: @@ -225,7 +223,7 @@ def add_rule( ) self._expand(comb_class, child_label, old_strategies, False) # apply tracking strategies - if comb_class.assumptions and child_label not in self.tracked_expanded: + if comb_class.parameters and child_label not in self.tracked_expanded: self.tracked_expanded.add(child_label) self._expand(comb_class, child_label, self.tracking_strategies, False) if not rule.inferrable: @@ -246,12 +244,12 @@ def add_rule( self.ruledb.add(start_label, end_labels, rule) if not isinstance( rule.strategy, - (RearrangeAssumptionStrategy, AddAssumptionsStrategy), + (RearrangeAssumptionStrategy, AddParametersStrategy), ): self.store_strategy(start_label, rule.strategy) for label in list(self.tilings_from_underlying[start_label]): - assumption_tiling = self.classdb.get_class(label) - self._expand(assumption_tiling, label, (rule.strategy,), False) + parameter_tiling = self.classdb.get_class(label) + self._expand(parameter_tiling, label, (rule.strategy,), False) class ForgetTrackedSearcher(TrackedSearcher): @@ -292,7 +290,7 @@ def _expand_class_with_strategy( initial: bool = False, ) -> Iterator[Tuple[int, Tuple[int, ...], AbstractRule]]: if not comb_class.parameters and not isinstance( - strategy_generator, (AddAssumptionFactory, RearrangeAssumptionFactory) + strategy_generator, (AddParameterFactory, RearrangeAssumptionFactory) ): idx = self.strategies.index(strategy_generator) assert isinstance(label, int) From bdaf0f7faa5b7479590662782279352b07592b76 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Tue, 23 Nov 2021 12:53:16 +0000 Subject: [PATCH 34/41] only verify things without parameters (except points) (#397) --- tilings/strategies/verification.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tilings/strategies/verification.py b/tilings/strategies/verification.py index 35a4f0cf..865c1cce 100644 --- a/tilings/strategies/verification.py +++ b/tilings/strategies/verification.py @@ -166,7 +166,7 @@ def pack(comb_class: Tiling) -> StrategyPack: ) def verified(self, comb_class: Tiling) -> bool: - if not comb_class.dimensions == (1, 1): + if not comb_class.dimensions == (1, 1) or comb_class.parameters: return False if not self.basis: return True @@ -345,6 +345,7 @@ def _locally_factorable_requirements(tiling: Tiling): def verified(self, comb_class: Tiling): return ( not comb_class.dimensions == (1, 1) + and not comb_class.parameters and self._locally_factorable_obstructions(comb_class) and self._locally_factorable_requirements(comb_class) ) @@ -407,7 +408,11 @@ class ElementaryVerificationStrategy(LocallyFactorableVerificationStrategy): @staticmethod def verified(comb_class: Tiling): - return comb_class.fully_isolated() and not comb_class.dimensions == (1, 1) + return ( + not comb_class.parameters + and comb_class.fully_isolated() + and not comb_class.dimensions == (1, 1) + ) @staticmethod def formal_step() -> str: @@ -457,6 +462,7 @@ def pack(self, comb_class: Tiling) -> StrategyPack: def verified(self, comb_class: Tiling) -> bool: return ( comb_class.dimensions != (1, 1) + and not comb_class.parameters and (not self.no_factors or len(comb_class.find_factors()) == 1) and LocalEnumeration(comb_class).verified() ) @@ -610,7 +616,9 @@ def pack(self, comb_class: Tiling) -> StrategyPack: def verified(self, comb_class: Tiling) -> bool: return ( - not self.no_factors or len(comb_class.find_factors()) == 1 + not comb_class.parameters + and not self.no_factors + or len(comb_class.find_factors()) == 1 ) and MonotoneTreeEnumeration(comb_class).verified() @staticmethod From bb386a71b7ae772a960a7ba41d8f3f240273d952 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Tue, 30 Nov 2021 14:14:03 +0000 Subject: [PATCH 35/41] Rearrange parameter strategy (#401) * updating rearrange to work with parameters * update tests, and fix small issues * rearrange_assumption -> rearrange_parameter * forgot to add the changed file names!! * revert adding rowcolmap, param, preim to init due to circular import --- ...umption.py => test_rearrange_parameter.py} | 55 +++--- tilings/__init__.py | 6 +- tilings/strategies/__init__.py | 4 +- ...e_assumption.py => rearrange_parameter.py} | 169 ++++++++++-------- tilings/strategy_pack.py | 2 +- tilings/tilescope.py | 10 +- tox.ini | 2 +- 7 files changed, 140 insertions(+), 108 deletions(-) rename tests/strategies/{test_rearrange_assumption.py => test_rearrange_parameter.py} (69%) rename tilings/strategies/{rearrange_assumption.py => rearrange_parameter.py} (71%) diff --git a/tests/strategies/test_rearrange_assumption.py b/tests/strategies/test_rearrange_parameter.py similarity index 69% rename from tests/strategies/test_rearrange_assumption.py rename to tests/strategies/test_rearrange_parameter.py index 730b5176..6ea72dec 100644 --- a/tests/strategies/test_rearrange_assumption.py +++ b/tests/strategies/test_rearrange_parameter.py @@ -2,44 +2,55 @@ import sympy from tilings import GriddedPerm, Tiling -from tilings.assumptions import TrackingAssumption -from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy +from tilings.map import RowColMap +from tilings.parameter_counter import ParameterCounter, PreimageCounter +from tilings.strategies.rearrange_parameter import RearrangeParameterStrategy + + +def columntopreimage(col: int, tiling: Tiling) -> PreimageCounter: + rowmap = {i: i for i in range(tiling.dimensions[1])} + colmap = {i: i for i in range(col + 1)} + for i in range(col + 1, tiling.dimensions[0] + 1): + colmap[i] = i - 1 + rowcolmap = RowColMap(rowmap, colmap) + return PreimageCounter(rowcolmap.preimage_tiling(tiling), rowcolmap) @pytest.fixture def rule1(): - ass1 = TrackingAssumption([GriddedPerm((0,), ((0, 0),))]) - ass2 = TrackingAssumption( - [GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))] - ) - strat = RearrangeAssumptionStrategy(ass2, ass1) - t1 = Tiling( + tiling = Tiling( obstructions=[ GriddedPerm((0, 1), ((0, 0),) * 2), GriddedPerm((0, 1), ((1, 0),) * 2), - ], - assumptions=[ass1, ass2], + ] + ) + param1 = ParameterCounter([columntopreimage(0, tiling)]) + param2 = ParameterCounter( + [columntopreimage(0, tiling), columntopreimage(1, tiling)] ) - return strat(t1) + tiling = tiling.add_parameters([param1, param2]) + strat = RearrangeParameterStrategy(param2, param1) + + return strat(tiling) @pytest.fixture def rule2(): - ass1 = TrackingAssumption([GriddedPerm((0,), ((0, 0),))]) - ass2 = TrackingAssumption( - [GriddedPerm((0,), ((0, 0),)), GriddedPerm((0,), ((1, 0),))] - ) - ass3 = TrackingAssumption([GriddedPerm((0,), ((1, 0),))]) - strat = RearrangeAssumptionStrategy(ass2, ass1) - t1 = Tiling( + tiling = Tiling( obstructions=[ GriddedPerm((0, 1), ((0, 0),) * 2), GriddedPerm((0, 1), ((1, 0),) * 2), - ], - assumptions=[ass1, ass2], + ] + ) + param1 = ParameterCounter([columntopreimage(0, tiling)]) + param2 = ParameterCounter( + [columntopreimage(0, tiling), columntopreimage(1, tiling)] ) - t2 = t1.add_assumption(ass3) - return strat(t2) + param3 = ParameterCounter([columntopreimage(1, tiling)]) + strat = RearrangeParameterStrategy(param2, param1) + tiling = tiling.add_parameters([param1, param2]) + tiling2 = tiling.add_parameters([param3]) + return strat(tiling2) def test_extra_param(rule1, rule2): diff --git a/tilings/__init__.py b/tilings/__init__.py index 4efd7758..c78a3071 100644 --- a/tilings/__init__.py +++ b/tilings/__init__.py @@ -4,4 +4,8 @@ __version__ = "3.0.0" -__all__ = ["GriddedPerm", "Tiling", "TrackingAssumption"] +__all__ = [ + "GriddedPerm", + "Tiling", + "TrackingAssumption", +] diff --git a/tilings/strategies/__init__.py b/tilings/strategies/__init__.py index 5b7076df..1a90e5ef 100644 --- a/tilings/strategies/__init__.py +++ b/tilings/strategies/__init__.py @@ -19,7 +19,7 @@ RemoveIdentityPreimageStrategy, RemoveReqFactory, ) -from .rearrange_assumption import RearrangeAssumptionFactory +from .rearrange_parameter import RearrangeParameterFactory from .requirement_insertion import ( CellInsertionFactory, FactorInsertionFactory, @@ -55,7 +55,7 @@ "DetectComponentsStrategy", "DisjointUnionParameterFactory", "ParameterVerificationStrategy", - "RearrangeAssumptionFactory", + "RearrangeParameterFactory", "RemoveIdentityPreimageStrategy", "RemoveReqFactory", "SplittingStrategy", diff --git a/tilings/strategies/rearrange_assumption.py b/tilings/strategies/rearrange_parameter.py similarity index 71% rename from tilings/strategies/rearrange_assumption.py rename to tilings/strategies/rearrange_parameter.py index e1ade858..ce8cb468 100644 --- a/tilings/strategies/rearrange_assumption.py +++ b/tilings/strategies/rearrange_parameter.py @@ -18,53 +18,71 @@ Terms, ) from tilings import GriddedPerm, Tiling -from tilings.assumptions import TrackingAssumption +from tilings.parameter_counter import ParameterCounter Cell = Tuple[int, int] +class MultiSet(Counter): + def subset_of(self, other: "MultiSet"): + return all(val <= other[key] for key, val in self.items()) + + def set_minus(self, other: "MultiSet") -> "MultiSet": + minus = MultiSet() + for key, val in self.items(): + new_val = val - other[key] + if new_val > 0: + minus[key] = new_val + return minus + + def __iter__(self) -> Iterator: + for key, val in self.items(): + for _ in range(val): + yield key + + class RearrangeConstructor(Constructor[Tiling, GriddedPerm]): def __init__( self, parent: Tiling, child: Tiling, - assumption: TrackingAssumption, - sub_assumption: TrackingAssumption, + parameter: ParameterCounter, + sub_parameter: ParameterCounter, extra_parameters: Dict[str, str], ): """ Constructor for the rearrange strategy. The extra_parameters should a dict mapping variable on the parent to the - associated variable on the child. The variable for `assumption` should not + associated variable on the child. The variable for `parameter` should not appear in the dict since it does not match directly to a variable on the child. """ - new_ass = TrackingAssumption(set(assumption.gps) - set(sub_assumption.gps)) - self.new_ass_child_idx = child.extra_parameters.index( - child.get_assumption_parameter(new_ass) + new_param = ParameterCounter(MultiSet(parameter) - MultiSet(sub_parameter)) + self.new_param_child_idx = child.extra_parameters.index( + child.get_parameter_name(new_param) ) - self.ass_parent_idx = parent.extra_parameters.index( - parent.get_assumption_parameter(assumption) + self.param_parent_idx = parent.extra_parameters.index( + parent.get_parameter_name(parameter) ) - self.subass_parent_idx = parent.extra_parameters.index( - parent.get_assumption_parameter(sub_assumption) + self.subparam_parent_idx = parent.extra_parameters.index( + parent.get_parameter_name(sub_parameter) ) - self.subass_child_idx = child.extra_parameters.index( - child.get_assumption_parameter(sub_assumption) + self.subparam_child_idx = child.extra_parameters.index( + child.get_parameter_name(sub_parameter) ) self.child_to_parent_param_map = self._build_child_to_parent_param_map( parent, child, extra_parameters, - assumption, - sub_assumption, + parameter, + sub_parameter, ) self.parent_to_child_param_map = self._build_parent_to_child_param_map( parent, child, extra_parameters, - assumption, - sub_assumption, + parameter, + sub_parameter, ) self.parent_dict_to_param = self._build_map_dict_to_param(parent) self.child_param_to_dict = self._build_map_param_to_dict(child) @@ -75,8 +93,8 @@ def _build_child_to_parent_param_map( parent: Tiling, child: Tiling, extra_parameters: Dict[str, str], - assumptions: TrackingAssumption, - sub_assumption: TrackingAssumption, + parameters: ParameterCounter, + sub_parameter: ParameterCounter, ) -> ParametersMap: """ Build a maps that maps parameters on the child to the corresponding parameters @@ -89,10 +107,10 @@ def _build_child_to_parent_param_map( child_pos_to_parent_pos: List[Tuple[int, ...]] = [] for pos, param in enumerate(child.extra_parameters): to_add: List[int] = [] - if pos == self.subass_child_idx: - to_add.append(self.ass_parent_idx) - elif pos == self.new_ass_child_idx: - to_add.append(self.ass_parent_idx) + if pos == self.subparam_child_idx: + to_add.append(self.param_parent_idx) + elif pos == self.new_param_child_idx: + to_add.append(self.param_parent_idx) if param in reversed_extra_param: to_add.append(parent_param_to_pos[reversed_extra_param[param]]) child_pos_to_parent_pos.append(tuple(to_add)) @@ -109,9 +127,9 @@ def param_map_for_rearrange( new_param = [-1 for _ in range(num_child_param)] for ppos, cpos in parent_pos_to_child_pos: new_param[cpos] = param[ppos] - new_ass_value = param[self.ass_parent_idx] - param[self.subass_parent_idx] - assert new_param[self.new_ass_child_idx] in (-1, new_ass_value) - new_param[self.new_ass_child_idx] = new_ass_value + new_param_value = param[self.param_parent_idx] - param[self.subparam_parent_idx] + assert new_param[self.new_param_child_idx] in (-1, new_param_value) + new_param[self.new_param_child_idx] = new_param_value assert all(v >= 0 for v in new_param) return tuple(new_param) @@ -120,8 +138,8 @@ def _build_parent_to_child_param_map( parent: Tiling, child: Tiling, extra_parameters: Dict[str, str], - assumptions: TrackingAssumption, - sub_assumption: TrackingAssumption, + parameters: ParameterCounter, + sub_parameter: ParameterCounter, ) -> ParametersMap: """ Build a maps that maps parameters on the parent to the corresponding parameters @@ -190,11 +208,13 @@ def _build_eq_subs( sympy.var(child): sympy.var(parent) for parent, child in extra_parameters.items() } - new_ass_var_child = sympy.var(child.extra_parameters[self.new_ass_child_idx]) - ass_var_parent = sympy.var(parent.extra_parameters[self.ass_parent_idx]) - subass_var_child = sympy.var(child.extra_parameters[self.subass_child_idx]) - subs[new_ass_var_child] = subs.get(new_ass_var_child, 1) * ass_var_parent - subs[subass_var_child] *= ass_var_parent + new_param_var_child = sympy.var( + child.extra_parameters[self.new_param_child_idx] + ) + param_var_parent = sympy.var(parent.extra_parameters[self.param_parent_idx]) + subparam_var_child = sympy.var(child.extra_parameters[self.subparam_child_idx]) + subs[new_param_var_child] = subs.get(new_param_var_child, 1) * param_var_parent + subs[subparam_var_child] *= param_var_parent return subs def get_equation( @@ -249,11 +269,11 @@ def __init__( self, parent: Tiling, child: Tiling, - assumption: TrackingAssumption, - sub_assumption: TrackingAssumption, + parameter: ParameterCounter, + sub_parameter: ParameterCounter, extra_parameters: Dict[str, str], ): - super().__init__(parent, child, assumption, sub_assumption, extra_parameters) + super().__init__(parent, child, parameter, sub_parameter, extra_parameters) self.child_to_parent_param_map, self.parent_to_child_param_map = ( self.parent_to_child_param_map, self.child_to_parent_param_map, @@ -267,12 +287,10 @@ def get_equation( return super().get_equation(rhs_funcs[0], (lhs_func,)) -class RearrangeAssumptionStrategy(Strategy[Tiling, GriddedPerm]): - def __init__( - self, assumption: TrackingAssumption, sub_assumption: TrackingAssumption - ): - self.assumption = assumption - self.sub_assumption = sub_assumption +class RearrangeParameterStrategy(Strategy[Tiling, GriddedPerm]): + def __init__(self, parameter: ParameterCounter, sub_parameter: ParameterCounter): + self.parameter = parameter + self.sub_parameter = sub_parameter super().__init__() @staticmethod @@ -302,21 +320,21 @@ def reverse_constructor( if children is None: children = self.decomposition_function(comb_class) if children is None: - raise StrategyDoesNotApply("Can't split the tracking assumption") + raise StrategyDoesNotApply("Can't rearrange the parameter") return ReverseRearrangeConstructor( comb_class, children[0], - self.assumption, - self.sub_assumption, + self.parameter, + self.sub_parameter, self.extra_parameters(comb_class, children)[0], ) def decomposition_function(self, comb_class: Tiling) -> Tuple[Tiling]: - tiling = comb_class.remove_assumption(self.assumption) - new_ass1 = TrackingAssumption( - set(self.assumption.gps) - set(self.sub_assumption.gps) + tiling = comb_class.remove_parameter(self.parameter) + new_param1 = ParameterCounter( + MultiSet(self.parameter) - MultiSet(self.sub_parameter) ) - return (tiling.add_assumption(new_ass1),) + return (tiling.add_parameter(new_param1),) def constructor( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None @@ -324,12 +342,12 @@ def constructor( if children is None: children = self.decomposition_function(comb_class) if children is None: - raise StrategyDoesNotApply("Can't split the tracking assumption") + raise StrategyDoesNotApply("Can't rearrange the parameter") return RearrangeConstructor( comb_class, children[0], - self.assumption, - self.sub_assumption, + self.parameter, + self.sub_parameter, self.extra_parameters(comb_class, children)[0], ) @@ -342,17 +360,16 @@ def extra_parameters( raise StrategyDoesNotApply("Strategy does not apply") res: Dict[str, str] = {} child = children[0] - for parent_ass, parent_param in zip( - comb_class.assumptions, comb_class.extra_parameters + for parent_param, parent_param_name in zip( + comb_class.parameters, comb_class.extra_parameters ): - if parent_ass == self.assumption: + if parent_param == self.parameter: continue - child_param = child.extra_parameters[child.assumptions.index(parent_ass)] - res[parent_param] = child_param + res[parent_param_name] = child.get_parameter_name(parent_param) return (res,) def formal_step(self) -> str: - return f"rearranging the assumption {self.assumption} and {self.sub_assumption}" + return f"rearranging the parameter {self.parameter} and {self.sub_parameter}" def backward_map( self, @@ -389,50 +406,50 @@ def to_jsonable(self) -> dict: d.pop("inferrable") d.pop("possibly_empty") d.pop("workable") - d["assumption"] = self.assumption.to_jsonable() - d["sub_assumption"] = self.sub_assumption.to_jsonable() + d["parameter"] = self.parameter.to_jsonable() + d["sub_parameter"] = self.sub_parameter.to_jsonable() return d def __repr__(self) -> str: args = ", ".join( [ - f"assumption={self.assumption!r}", - f"sub_assumption={self.sub_assumption!r}", + f"parameter={self.parameter!r}", + f"sub_parameter={self.sub_parameter!r}", ] ) return f"{self.__class__.__name__}({args})" @classmethod - def from_dict(cls, d: dict) -> "RearrangeAssumptionStrategy": - assumption = TrackingAssumption.from_dict(d.pop("assumption")) - sub_assumption = TrackingAssumption.from_dict(d.pop("sub_assumption")) + def from_dict(cls, d: dict) -> "RearrangeParameterStrategy": + parameter = ParameterCounter.from_dict(d.pop("parameter")) + sub_parameter = ParameterCounter.from_dict(d.pop("sub_parameter")) assert not d - return cls(assumption, sub_assumption) + return cls(parameter, sub_parameter) @staticmethod def get_eq_symbol() -> str: return "↣" -class RearrangeAssumptionFactory(StrategyFactory[Tiling]): - def __call__(self, comb_class: Tiling) -> Iterator[RearrangeAssumptionStrategy]: - assumptions = comb_class.assumptions - for ass1, ass2 in combinations(assumptions, 2): - if set(ass1.gps).issubset(set(ass2.gps)): - yield RearrangeAssumptionStrategy(ass2, ass1) - if set(ass2.gps).issubset(set(ass1.gps)): - yield RearrangeAssumptionStrategy(ass1, ass2) +class RearrangeParameterFactory(StrategyFactory[Tiling]): + def __call__(self, comb_class: Tiling) -> Iterator[RearrangeParameterStrategy]: + parameters = comb_class.parameters + for param1, param2 in combinations(parameters, 2): + if MultiSet(param1).subset_of(MultiSet(param2)): + yield RearrangeParameterStrategy(param2, param1) + if MultiSet(param2).subset_of(MultiSet(param1)): + yield RearrangeParameterStrategy(param1, param2) def __repr__(self) -> str: return self.__class__.__name__ + "()" def __str__(self) -> str: - return "rearrange assumptions" + return "rearrange parameters" def to_jsonable(self) -> dict: d: dict = super().to_jsonable() return d @classmethod - def from_dict(cls, d: dict) -> "RearrangeAssumptionFactory": + def from_dict(cls, d: dict) -> "RearrangeParameterFactory": return cls() diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index 9643f250..7ed2b500 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -182,7 +182,7 @@ def make_interleaving( interleaving factor strategy. If unions is set to True it will overwrite unions on the strategy, and - also pass the argument to AddInterleavingAssumption method. + also pass the argument to AddInterleavingParameter method. """ def replace_list(strats): diff --git a/tilings/tilescope.py b/tilings/tilescope.py index caf78063..1b63ad7f 100644 --- a/tilings/tilescope.py +++ b/tilings/tilescope.py @@ -16,9 +16,9 @@ from comb_spec_searcher.typing import CombinatorialClassType, CSSstrategy from permuta import Basis, Perm from tilings import GriddedPerm, Tiling -from tilings.strategies import AddParameterFactory, RearrangeAssumptionFactory +from tilings.strategies import AddParameterFactory, RearrangeParameterFactory from tilings.strategies.parameter_insertion import AddParametersStrategy -from tilings.strategies.rearrange_assumption import RearrangeAssumptionStrategy +from tilings.strategies.rearrange_parameter import RearrangeParameterStrategy from tilings.strategy_pack import TileScopePack __all__ = ("TileScope", "TileScopePack", "LimitedParameterTileScope", "GuidedSearcher") @@ -170,7 +170,7 @@ def __init__(self, *args, **kwargs): self.tilings_from_underlying: DefaultDict[int, Set[int]] = defaultdict(set) self.tracking_strategies = [ AddParameterFactory(), - RearrangeAssumptionFactory(), + RearrangeParameterFactory(), ] self.tracked_expanded: Set[int] = set() self.retroactively_expanded: Set[int] = set() @@ -244,7 +244,7 @@ def add_rule( self.ruledb.add(start_label, end_labels, rule) if not isinstance( rule.strategy, - (RearrangeAssumptionStrategy, AddParametersStrategy), + (RearrangeParameterStrategy, AddParametersStrategy), ): self.store_strategy(start_label, rule.strategy) for label in list(self.tilings_from_underlying[start_label]): @@ -290,7 +290,7 @@ def _expand_class_with_strategy( initial: bool = False, ) -> Iterator[Tuple[int, Tuple[int, ...], AbstractRule]]: if not comb_class.parameters and not isinstance( - strategy_generator, (AddParameterFactory, RearrangeAssumptionFactory) + strategy_generator, (AddParameterFactory, RearrangeParameterFactory) ): idx = self.strategies.index(strategy_generator) assert isinstance(label, int) diff --git a/tox.ini b/tox.ini index 1fd89653..f40be9c3 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = pytest [pytest] ; Should remove the ignore and add the readme to test path before merging into develop. -addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_rearrange_assumption.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py --ignore=tests/strategies/test_sliding.py +addopts = --doctest-modules --doctest-ignore-import-errors --ignore=tests/algorithms/test_sliding_alg.py --ignore=tests/strategies/test_assumtions_splitting.py --ignore=tests/strategies/test_encoding.py --ignore=tests/strategies/test_reverse_fusion.py --ignore=tests/strategies/test_constructor_equiv.py --ignore=tests/strategies/test_symmetries.py --ignore=tests/strategies/test_verification.py --ignore=tests/strategies/test_sliding.py testpaths = tests tilings markers = slow: marks tests as slow (deselect with '-m "not slow"') doctest_optionflags= NORMALIZE_WHITESPACE From 744845d27bb989ab722de2dd2413c672c57cff36 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 2 Dec 2021 19:15:47 +0000 Subject: [PATCH 36/41] add rearrange strategy to the fusion packs --- tilings/strategy_pack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index b35deb4d..349a4e1e 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -170,6 +170,7 @@ def make_fusion( pack = pack.add_initial( strat.RemoveIdentityPreimageStrategy(), apply_first=True ) + pack = pack.add_initial(strat.RearrangeParameterFactory(), apply_first=True) pack = pack.add_initial(strat.AddParameterFactory(), apply_first=True) pack = pack.add_verification(strat.ParameterVerificationStrategy()) return pack @@ -225,6 +226,7 @@ def replace_list(strats): pack = pack.add_initial( strat.RemoveIdentityPreimageStrategy(), apply_first=True ) + pack = pack.add_initial(strat.RearrangeParameterFactory(), apply_first=True) pack = pack.add_initial(strat.AddParameterFactory(), apply_first=True) pack = pack.add_verification(strat.ParameterVerificationStrategy()) return pack From f12b3eec75230ca2ef32295f7234f4f0947db7fb Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 3 Dec 2021 15:19:54 +0000 Subject: [PATCH 37/41] Req ins (#406) * tidy up insertion, and target size two obs for insertions * fix a test * assumptions -> parameters * need to special remove req, yuk * add remove req to packs * compare lhs to preim in disjoint param --- .../algorithms/test_simplify_gridded_perms.py | 1 - tests/test_parameter_rule.py | 7 +- tilings/strategies/__init__.py | 6 +- tilings/strategies/parameter_strategies.py | 114 ++++++++---------- tilings/strategies/requirement_insertion.py | 29 ++++- tilings/strategy_pack.py | 18 ++- tilings/tiling.py | 2 +- 7 files changed, 103 insertions(+), 74 deletions(-) diff --git a/tests/algorithms/test_simplify_gridded_perms.py b/tests/algorithms/test_simplify_gridded_perms.py index 5a432793..c63eedb7 100644 --- a/tests/algorithms/test_simplify_gridded_perms.py +++ b/tests/algorithms/test_simplify_gridded_perms.py @@ -233,7 +233,6 @@ def test_reduce_but_keep_bigger_sub_ob(): GriddedPerm((1, 0), ((0, 0), (0, 0))), ), requirements=((GriddedPerm((0,), ((1, 2),)), GriddedPerm((0,), ((2, 1),))),), - assumptions=(), ) assert t == expected diff --git a/tests/test_parameter_rule.py b/tests/test_parameter_rule.py index 9233c516..5012f9ea 100644 --- a/tests/test_parameter_rule.py +++ b/tests/test_parameter_rule.py @@ -4,8 +4,9 @@ from tilings import GriddedPerm, Tiling from tilings.map import RowColMap from tilings.parameter_counter import ParameterCounter, PreimageCounter +from tilings.strategies import RemoveRequirementFactory from tilings.strategies.fusion import FusionFactory, FusionStrategy -from tilings.strategies.parameter_strategies import RemoveReqFactory +from tilings.strategies.parameter_strategies import DisjointUnionParameterFactory def test_counting_fusion_rule(): @@ -176,7 +177,7 @@ def test_remove_req_identity(): ), ) with pytest.raises(StopIteration): - next(RemoveReqFactory()(t)) + next(DisjointUnionParameterFactory(RemoveRequirementFactory())(t)) def test_remove_req_factory(): @@ -202,7 +203,7 @@ def test_remove_req_factory(): ), ), ) - strat = RemoveReqFactory() + strat = DisjointUnionParameterFactory(RemoveRequirementFactory()) assert sum(1 for _ in strat(t)) == 1 assert next(strat(t)).children == ( Tiling( diff --git a/tilings/strategies/__init__.py b/tilings/strategies/__init__.py index 25a521ec..58bb7cc5 100644 --- a/tilings/strategies/__init__.py +++ b/tilings/strategies/__init__.py @@ -17,12 +17,12 @@ DisjointUnionParameterFactory, ParameterVerificationStrategy, RemoveIdentityPreimageStrategy, - RemoveReqFactory, ) from .rearrange_parameter import RearrangeParameterFactory from .requirement_insertion import ( CellInsertionFactory, FactorInsertionFactory, + FactorSizeTwoObstructionInsertionFactory, RemoveRequirementFactory, RequirementCorroborationFactory, RequirementExtensionFactory, @@ -58,12 +58,12 @@ "ParameterVerificationStrategy", "RearrangeParameterFactory", "RemoveIdentityPreimageStrategy", - "RemoveReqFactory", "SplittingStrategy", # Batch + "AllPlacementsFactory", "CellInsertionFactory", "FactorInsertionFactory", - "AllPlacementsFactory", + "FactorSizeTwoObstructionInsertionFactory", "RemoveRequirementFactory", "RequirementExtensionFactory", "RequirementInsertionFactory", diff --git a/tilings/strategies/parameter_strategies.py b/tilings/strategies/parameter_strategies.py index 75469760..3d936aa4 100644 --- a/tilings/strategies/parameter_strategies.py +++ b/tilings/strategies/parameter_strategies.py @@ -1,5 +1,5 @@ from collections import Counter -from typing import Callable, Dict, Iterator, List, Optional, Tuple, cast +from typing import Callable, Dict, Iterator, List, Optional, Tuple, Union, cast import sympy @@ -29,7 +29,11 @@ ) from tilings import GriddedPerm, Tiling from tilings.parameter_counter import ParameterCounter, PreimageCounter -from tilings.strategies.requirement_insertion import RequirementInsertionStrategy + +from .requirement_insertion import ( + RemoveRequirementFactory, + RequirementInsertionStrategy, +) class RemoveIdentityPreimageConstructor(Constructor): @@ -438,14 +442,59 @@ def __init__(self, strategy: CSSstrategy): self.strategy = strategy super().__init__() - def __call__(self, comb_class: Tiling) -> Iterator[DisjointUnionStrategy]: + def __call__( + self, comb_class: Tiling + ) -> Iterator[Union[DisjointUnionStrategy, Rule]]: for i, param in enumerate(comb_class.parameters): for j, preimage in enumerate(param.counters): for rule in CombinatorialSpecificationSearcher._rules_from_strategy( preimage.tiling, self.strategy ): assert isinstance(rule.strategy, DisjointUnionStrategy) - yield DisjointParameterStrategy(rule.strategy, i, j) + if rule.comb_class == preimage.tiling: + yield DisjointParameterStrategy(rule.strategy, i, j) + elif isinstance(self.strategy, RemoveRequirementFactory): + assert isinstance(rule.strategy, RequirementInsertionStrategy) + yield from self._special_case_remove_requirement_factory( + comb_class, rule.strategy, i, j + ) + + @staticmethod + def _special_case_remove_requirement_factory( + comb_class: Tiling, strategy: RequirementInsertionStrategy, i: int, j: int + ) -> Iterator[Rule]: + """TODO: this is a major special case to reduce work done""" + param = comb_class.parameters[i] + preimage = param.counters[j] + req = tuple(sorted(strategy.gps)) + req_idx = preimage.tiling.requirements.index(req) + image_req = tuple(sorted(preimage.map.map_gps(req))) + preimage_image_req = tuple(sorted(preimage.map.preimage_gps(image_req))) + if image_req in comb_class.requirements and preimage_image_req == req: + return + new_tiling = Tiling( + preimage.tiling.obstructions, + preimage.tiling.requirements[:req_idx] + + preimage.tiling.requirements[req_idx + 1 :] + + ((preimage_image_req,) if preimage_image_req != req else tuple()), + ) + new_preimage = PreimageCounter(new_tiling, preimage.map) + new_param = ParameterCounter( + comb_class.parameters[i].counters[:j] + + comb_class.parameters[i].counters[j + 1 :] + + (new_preimage,) + ) + new_comb_class = comb_class.remove_parameter(param).add_parameter(new_param) + if any( + PreimageCounter(child, preimage.map).always_counts_one(comb_class) + for child in strategy(new_tiling).children + ): + yield DisjointParameterStrategy( + strategy, + new_comb_class.parameters.index(new_param), + new_param.counters.index(new_preimage), + False, + )(new_comb_class) def __str__(self) -> str: return f"applying '{self.strategy}' to parameters" @@ -518,60 +567,3 @@ def to_jsonable(self) -> dict: def __str__(self) -> str: return "parameter verification" - - -class RemoveReqFactory(StrategyFactory[Tiling]): - def __call__(self, comb_class: Tiling) -> Iterator[Rule]: - for param_idx, param in enumerate(comb_class.parameters): - for preimg_idx, preimg in enumerate(param): - for req_idx, req in enumerate(preimg.tiling.requirements): - image_req = tuple(sorted(preimg.map.map_gps(req))) - preimage_image_req = tuple( - sorted(preimg.map.preimage_gps(image_req)) - ) - if ( - image_req in comb_class.requirements - and preimage_image_req == req - ): - continue - new_tiling = Tiling( - preimg.tiling.obstructions, - preimg.tiling.requirements[:req_idx] - + preimg.tiling.requirements[req_idx + 1 :] - + ( - (preimage_image_req,) - if preimage_image_req != req - else tuple() - ), - ) - new_preimage = PreimageCounter(new_tiling, preimg.map) - new_param = ParameterCounter( - comb_class.parameters[param_idx].counters[:preimg_idx] - + comb_class.parameters[param_idx].counters[preimg_idx + 1 :] - + (new_preimage,) - ) - new_comb_class = comb_class.remove_parameter(param).add_parameter( - new_param - ) - param_strategy = RequirementInsertionStrategy(req) - if any( - PreimageCounter(child, preimg.map).always_counts_one(comb_class) - for child in param_strategy(new_tiling).children - ): - yield DisjointParameterStrategy( - param_strategy, - new_comb_class.parameters.index(new_param), - new_param.counters.index(new_preimage), - False, - )(new_comb_class) - - def __str__(self) -> str: - return "Remove requirements from preimages" - - def __repr__(self) -> str: - raise NotImplementedError - - @classmethod - def from_dict(cls, d: dict) -> "RemoveReqFactory": - assert not d - return cls() diff --git a/tilings/strategies/requirement_insertion.py b/tilings/strategies/requirement_insertion.py index 5799e10d..194c4ee4 100644 --- a/tilings/strategies/requirement_insertion.py +++ b/tilings/strategies/requirement_insertion.py @@ -475,12 +475,39 @@ def req_lists_to_insert(self, tiling: Tiling) -> Iterator[ListRequirement]: gp_facts = map(GriddedPerm.factors, reqs_and_obs) proper_facts = chain.from_iterable(f for f in gp_facts if len(f) > 1) for f in proper_facts: - yield (GriddedPerm(f.patt, f.pos),) + if not any(f.contains(*req) for req in tiling.requirements): + yield (GriddedPerm(f.patt, f.pos),) def __str__(self) -> str: return "all factor insertion" +class FactorSizeTwoObstructionInsertionFactory(AbstractRequirementInsertionFactory): + """ + Insert factors of size two obstructions which are factorable. + """ + + def __init__(self, ignore_parent: bool = True) -> None: + super().__init__(ignore_parent) + + def req_lists_to_insert(self, tiling: Tiling) -> Iterator[ListRequirement]: + gp_facts = map( + GriddedPerm.factors, + ( + ob + for ob in tiling.obstructions + if len(ob) == 2 and not ob.is_single_cell() and not ob.is_interleaving() + ), + ) + proper_facts = chain.from_iterable(f for f in gp_facts if len(f) > 1) + for f in proper_facts: + if not any(f.contains(*req) for req in tiling.requirements): + yield (GriddedPerm(f.patt, f.pos),) + + def __str__(self) -> str: + return "targeted cell insertion into size two obs" + + class RequirementCorroborationFactory(AbstractRequirementInsertionFactory): """ The requirement corroboration strategy. diff --git a/tilings/strategy_pack.py b/tilings/strategy_pack.py index 349a4e1e..55665ade 100644 --- a/tilings/strategy_pack.py +++ b/tilings/strategy_pack.py @@ -163,10 +163,15 @@ def make_fusion( pack = self.add_initial(fusion_strat, name, apply_first=apply_first) if tracked: pack = pack.add_initial( - strat.DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + strat.DisjointUnionParameterFactory( + strat.FactorSizeTwoObstructionInsertionFactory() + ), apply_first=True, ) - pack = pack.add_initial(strat.RemoveReqFactory(), apply_first=True) + # TODO: CSS add initial doesn't allow two of the same strategy + pack.initial_strats = ( + strat.DisjointUnionParameterFactory(strat.RemoveRequirementFactory()), + ) + pack.initial_strats pack = pack.add_initial( strat.RemoveIdentityPreimageStrategy(), apply_first=True ) @@ -219,10 +224,15 @@ def replace_list(strats): strat.AddInterleavingParameterFactory(unions=unions), apply_first=True ) pack = pack.add_initial( - strat.DisjointUnionParameterFactory(strat.FactorInsertionFactory()), + strat.DisjointUnionParameterFactory( + strat.FactorSizeTwoObstructionInsertionFactory() + ), apply_first=True, ) - pack = pack.add_initial(strat.RemoveReqFactory(), apply_first=True) + # TODO: CSS add initial doesn't allow two of the same strategy + pack.initial_strats = ( + strat.DisjointUnionParameterFactory(strat.RemoveRequirementFactory()), + ) + pack.initial_strats pack = pack.add_initial( strat.RemoveIdentityPreimageStrategy(), apply_first=True ) diff --git a/tilings/tiling.py b/tilings/tiling.py index 97ca27ea..60bbd124 100644 --- a/tilings/tiling.py +++ b/tilings/tiling.py @@ -617,7 +617,7 @@ def remove_requirement(self, requirement: Tuple[GriddedPerm, ...]) -> "Tiling": return Tiling( self._obstructions, self._requirements[:idx] + self._requirements[idx + 1 :], - self._assumptions, + self._parameters, remove_empty_rows_and_cols=False, derive_empty=False, simplify=False, From 562b4a8e4074665aa8fb925210262b3e2d1109ff Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 3 Dec 2021 16:44:13 +0000 Subject: [PATCH 38/41] Mapped params (#408) * only try to map params if they dont become empty * add a test for the fixes --- tests/strategies/test_sanity_check.py | 35 ++++++++++++++++++++ tilings/strategies/obstruction_inferral.py | 5 +-- tilings/strategies/requirement_insertion.py | 10 +++--- tilings/strategies/requirement_placement.py | 14 ++++---- tilings/strategies/row_and_col_separation.py | 3 +- 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/tests/strategies/test_sanity_check.py b/tests/strategies/test_sanity_check.py index b8295f95..0fe7cfe7 100644 --- a/tests/strategies/test_sanity_check.py +++ b/tests/strategies/test_sanity_check.py @@ -1264,6 +1264,41 @@ ), ) ), + RequirementInsertionStrategy( + gps=frozenset({GriddedPerm((0,), ((0, 0),))}), ignore_parent=False + )( + Tiling( + obstructions=(GriddedPerm((1, 0, 3, 2), ((0, 0), (0, 0), (0, 0), (0, 0))),), + requirements=((GriddedPerm((0,), ((0, 0),)),),), + parameters=( + ParameterCounter( + ( + PreimageCounter( + Tiling( + obstructions=( + GriddedPerm((1, 0), ((1, 0), (1, 0))), + GriddedPerm( + (1, 0, 3, 2), ((0, 0), (0, 0), (0, 0), (0, 0)) + ), + GriddedPerm( + (1, 0, 3, 2), ((0, 0), (0, 0), (0, 0), (1, 0)) + ), + ), + requirements=( + ( + GriddedPerm((0,), ((0, 0),)), + GriddedPerm((0,), ((1, 0),)), + ), + ), + parameters=(), + ), + RowColMap({0: 0}, {0: 0, 1: 0}), + ), + ) + ), + ), + ) + ), ] equiv_rule_to_check = [r for r in rules_to_check if r.is_equivalence()] diff --git a/tilings/strategies/obstruction_inferral.py b/tilings/strategies/obstruction_inferral.py index b6a6bd34..0bae6bba 100644 --- a/tilings/strategies/obstruction_inferral.py +++ b/tilings/strategies/obstruction_inferral.py @@ -50,8 +50,9 @@ def extra_parameters( self.gps, [] ).apply_row_col_map(child.forward_map) parent_var = comb_class.get_parameter_name(parameter) - child_var = child.get_parameter_name(mapped_parameter) - params[parent_var] = child_var + if mapped_parameter.counters: + child_var = child.get_parameter_name(mapped_parameter) + params[parent_var] = child_var return (params,) def backward_map( diff --git a/tilings/strategies/requirement_insertion.py b/tilings/strategies/requirement_insertion.py index 194c4ee4..9874b3eb 100644 --- a/tilings/strategies/requirement_insertion.py +++ b/tilings/strategies/requirement_insertion.py @@ -91,13 +91,15 @@ def extra_parameters( av_mapped_param = parameter.add_obstructions_and_requirements( self.gps, [] ).apply_row_col_map(av.forward_map) - child_var = av.get_parameter_name(av_mapped_param) - av_params[parent_var] = child_var + if av_mapped_param.counters: + child_var = av.get_parameter_name(av_mapped_param) + av_params[parent_var] = child_var co_mapped_param = parameter.add_obstructions_and_requirements( [], [self.gps] ).apply_row_col_map(co.forward_map) - child_var = co.get_parameter_name(co_mapped_param) - co_params[parent_var] = child_var + if co_mapped_param.counters: + child_var = co.get_parameter_name(co_mapped_param) + co_params[parent_var] = child_var return av_params, co_params def __repr__(self) -> str: diff --git a/tilings/strategies/requirement_placement.py b/tilings/strategies/requirement_placement.py index 36bb25d9..acf7c55b 100644 --- a/tilings/strategies/requirement_placement.py +++ b/tilings/strategies/requirement_placement.py @@ -96,8 +96,9 @@ def extra_parameters( self.gps, [] ).apply_row_col_map(child.forward_map) parent_var = comb_class.get_parameter_name(parameter) - child_var = child.get_parameter_name(mapped_parameter) - extra_parameters[0][parent_var] = child_var + if mapped_parameter.counters: + child_var = child.get_parameter_name(mapped_parameter) + extra_parameters[0][parent_var] = child_var for idx, (cell, child) in enumerate( zip(self._placed_cells, children[1:] if self.include_empty else children) ): @@ -124,10 +125,11 @@ def extra_parameters( comb_class.parameters, mapped_parameters ): parent_var = comb_class.get_parameter_name(parameter) - child_var = child.get_parameter_name(mapped_parameter) - extra_parameters[idx + 1 if self.include_empty else idx][ - parent_var - ] = child_var + if mapped_parameter.counters: + child_var = child.get_parameter_name(mapped_parameter) + extra_parameters[idx + 1 if self.include_empty else idx][ + parent_var + ] = child_var return extra_parameters def direction_string(self): diff --git a/tilings/strategies/row_and_col_separation.py b/tilings/strategies/row_and_col_separation.py index 7a68ccd4..4e0c4543 100644 --- a/tilings/strategies/row_and_col_separation.py +++ b/tilings/strategies/row_and_col_separation.py @@ -68,8 +68,7 @@ def extra_parameters( mapped_param ) for param, mapped_param in zip(comb_class.parameters, mapped_params) - # TODO: previsously checked that the tracked assumption had some gps - # here. What is the equivalence in our case. + if mapped_param.counters }, ) From c151284694686d7cca831d0cc35485b8b6da7ea3 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 3 Dec 2021 16:57:23 +0000 Subject: [PATCH 39/41] Restricted fusion (#407) * only fuse if param in region is exactly fuse param * pylint fix --- tilings/algorithms/fusion.py | 10 +++--- tilings/strategies/fusion/fusion.py | 55 +++++++++++++++-------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index a4018d02..497c7bda 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -89,8 +89,11 @@ def fused_obs_reqs_and_params(self) -> UninitializedTiling: def is_fusable_param(self, parameter_counter: ParameterCounter) -> bool: return all( - self.is_fusable_preimage(preimage) + not self._active_region_of_preimage_intersects_fuse_region(preimage) for preimage in parameter_counter.counters + ) or ( + len(parameter_counter.counters) == 1 + and parameter_counter.counters[0].tiling == self.allowed_preimage ) def _active_region_of_preimage_intersects_fuse_region( @@ -106,11 +109,6 @@ def _active_region_of_preimage_intersects_fuse_region( ) return bool(preimage.active_region(self.tiling).intersection(fuse_region)) - def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: - if not self._active_region_of_preimage_intersects_fuse_region(preimage): - return True - return preimage.tiling == self.allowed_preimage - @functools.cached_property def allowed_preimage(self) -> "Tiling": obs: List[GriddedPerm] = [] diff --git a/tilings/strategies/fusion/fusion.py b/tilings/strategies/fusion/fusion.py index 78044321..b927c555 100644 --- a/tilings/strategies/fusion/fusion.py +++ b/tilings/strategies/fusion/fusion.py @@ -10,7 +10,7 @@ from tilings import GriddedPerm, Tiling from tilings.algorithms import Fusion -from .constructor import FusionConstructor, ReverseFusionConstructor +from .constructor import FusionConstructor class FusionRule(NonBijectiveRule[Tiling, GriddedPerm]): @@ -211,32 +211,33 @@ def reverse_constructor( # pylint: disable=no-self-use comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None, ) -> Constructor: - if not self.tracked: - # constructor only enumerates when tracked. - raise NotImplementedError("The fusion strategy was not tracked.") - # Need to recompute some info to count, so ignoring passed in children - algo = self.fusion_algorithm(comb_class) - if not algo.fusable(): - raise StrategyDoesNotApply("Strategy does not apply") - if algo.min_left_right_points() != (0, 0): - raise NotImplementedError( - "Reverse positive fusion counting not implemented" - ) - child = algo.fused_tiling() - assert children is None or children == (child,) - ( - left_sided_params, - right_sided_params, - _, - ) = self.left_right_both_sided_parameters(comb_class) - return ReverseFusionConstructor( - comb_class, - child, - self._fuse_parameter_name(comb_class), - self.extra_parameters(comb_class, children)[0], - tuple(left_sided_params), - tuple(right_sided_params), - ) + raise NotImplementedError("Not fixed for parameters.") + # if not self.tracked: + # # constructor only enumerates when tracked. + # raise NotImplementedError("The fusion strategy was not tracked.") + # # Need to recompute some info to count, so ignoring passed in children + # algo = self.fusion_algorithm(comb_class) + # if not algo.fusable(): + # raise StrategyDoesNotApply("Strategy does not apply") + # if algo.min_left_right_points() != (0, 0): + # raise NotImplementedError( + # "Reverse positive fusion counting not implemented" + # ) + # child = algo.fused_tiling() + # assert children is None or children == (child,) + # ( + # left_sided_params, + # right_sided_params, + # _, + # ) = self.left_right_both_sided_parameters(comb_class) + # return ReverseFusionConstructor( + # comb_class, + # child, + # self._fuse_parameter_name(comb_class), + # self.extra_parameters(comb_class, children)[0], + # tuple(left_sided_params), + # tuple(right_sided_params), + # ) def extra_parameters( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None From 2e185781b89e19487dbcf0a773143e85951a4354 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Fri, 3 Dec 2021 17:22:26 +0000 Subject: [PATCH 40/41] Revert "Restricted fusion (#407)" This reverts commit c151284694686d7cca831d0cc35485b8b6da7ea3. --- tilings/algorithms/fusion.py | 10 +++--- tilings/strategies/fusion/fusion.py | 55 ++++++++++++++--------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/tilings/algorithms/fusion.py b/tilings/algorithms/fusion.py index 497c7bda..a4018d02 100644 --- a/tilings/algorithms/fusion.py +++ b/tilings/algorithms/fusion.py @@ -89,11 +89,8 @@ def fused_obs_reqs_and_params(self) -> UninitializedTiling: def is_fusable_param(self, parameter_counter: ParameterCounter) -> bool: return all( - not self._active_region_of_preimage_intersects_fuse_region(preimage) + self.is_fusable_preimage(preimage) for preimage in parameter_counter.counters - ) or ( - len(parameter_counter.counters) == 1 - and parameter_counter.counters[0].tiling == self.allowed_preimage ) def _active_region_of_preimage_intersects_fuse_region( @@ -109,6 +106,11 @@ def _active_region_of_preimage_intersects_fuse_region( ) return bool(preimage.active_region(self.tiling).intersection(fuse_region)) + def is_fusable_preimage(self, preimage: PreimageCounter) -> bool: + if not self._active_region_of_preimage_intersects_fuse_region(preimage): + return True + return preimage.tiling == self.allowed_preimage + @functools.cached_property def allowed_preimage(self) -> "Tiling": obs: List[GriddedPerm] = [] diff --git a/tilings/strategies/fusion/fusion.py b/tilings/strategies/fusion/fusion.py index b927c555..78044321 100644 --- a/tilings/strategies/fusion/fusion.py +++ b/tilings/strategies/fusion/fusion.py @@ -10,7 +10,7 @@ from tilings import GriddedPerm, Tiling from tilings.algorithms import Fusion -from .constructor import FusionConstructor +from .constructor import FusionConstructor, ReverseFusionConstructor class FusionRule(NonBijectiveRule[Tiling, GriddedPerm]): @@ -211,33 +211,32 @@ def reverse_constructor( # pylint: disable=no-self-use comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None, ) -> Constructor: - raise NotImplementedError("Not fixed for parameters.") - # if not self.tracked: - # # constructor only enumerates when tracked. - # raise NotImplementedError("The fusion strategy was not tracked.") - # # Need to recompute some info to count, so ignoring passed in children - # algo = self.fusion_algorithm(comb_class) - # if not algo.fusable(): - # raise StrategyDoesNotApply("Strategy does not apply") - # if algo.min_left_right_points() != (0, 0): - # raise NotImplementedError( - # "Reverse positive fusion counting not implemented" - # ) - # child = algo.fused_tiling() - # assert children is None or children == (child,) - # ( - # left_sided_params, - # right_sided_params, - # _, - # ) = self.left_right_both_sided_parameters(comb_class) - # return ReverseFusionConstructor( - # comb_class, - # child, - # self._fuse_parameter_name(comb_class), - # self.extra_parameters(comb_class, children)[0], - # tuple(left_sided_params), - # tuple(right_sided_params), - # ) + if not self.tracked: + # constructor only enumerates when tracked. + raise NotImplementedError("The fusion strategy was not tracked.") + # Need to recompute some info to count, so ignoring passed in children + algo = self.fusion_algorithm(comb_class) + if not algo.fusable(): + raise StrategyDoesNotApply("Strategy does not apply") + if algo.min_left_right_points() != (0, 0): + raise NotImplementedError( + "Reverse positive fusion counting not implemented" + ) + child = algo.fused_tiling() + assert children is None or children == (child,) + ( + left_sided_params, + right_sided_params, + _, + ) = self.left_right_both_sided_parameters(comb_class) + return ReverseFusionConstructor( + comb_class, + child, + self._fuse_parameter_name(comb_class), + self.extra_parameters(comb_class, children)[0], + tuple(left_sided_params), + tuple(right_sided_params), + ) def extra_parameters( self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None From e86d10b9de0732346afcfe7a2806da4f457d29f4 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Sat, 4 Dec 2021 14:48:24 +0000 Subject: [PATCH 41/41] Verification off (#396) * only verify things without parameters (except points) * adding tests for our successful trees (with xfail for now) * start tests * some more tests * having to specify packs to make universes smaller * skipping the domino :( --- tests/test_tilescope.py | 225 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 211 insertions(+), 14 deletions(-) diff --git a/tests/test_tilescope.py b/tests/test_tilescope.py index bbe7bf96..910cc9a3 100644 --- a/tests/test_tilescope.py +++ b/tests/test_tilescope.py @@ -8,9 +8,15 @@ from permuta import Av, Perm from tilings import GriddedPerm, Tiling from tilings import strategies as strat +from tilings.strategies import DisjointUnionParameterFactory from tilings.strategies.fusion import FusionStrategy from tilings.strategy_pack import TileScopePack -from tilings.tilescope import GuidedSearcher, TileScope +from tilings.tilescope import ( + GuidedSearcher, + LimitedParameterTileScope, + TileScope, + TrackedSearcher, +) class ComponentFusionStrategy: @@ -111,6 +117,78 @@ def test_123(): assert isinstance(spec, CombinatorialSpecification) +@pytest.mark.timeout(60) +def test_123_pp_fusion(): + pack = TileScopePack.point_placements().make_fusion(tracked=True) + # TODO: shouldn't need to remove the disjoint param strats + pack.initial_strats = tuple( + strat + for strat in pack.initial_strats + if not isinstance(strat, DisjointUnionParameterFactory) + ) + searcher = LimitedParameterTileScope( + (Perm((0, 1, 2)),), + pack, + max_parameters=1, + ) + spec = searcher.auto_search() + assert isinstance(spec, CombinatorialSpecification) + assert [spec.count_objects_of_size(i) for i in range(20)] == [ + 1, + 1, + 2, + 5, + 14, + 42, + 132, + 429, + 1430, + 4862, + 16796, + 58786, + 208012, + 742900, + 2674440, + 9694845, + 35357670, + 129644790, + 477638700, + 1767263190, + ] + + +@pytest.mark.timeout(20) +def test_123_row_fusion(): + searcher = TileScope( + (Perm((0, 1, 2)),), + TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True), + ) + spec = searcher.auto_search() + assert isinstance(spec, CombinatorialSpecification) + assert [spec.count_objects_of_size(i) for i in range(20)] == [ + 1, + 1, + 2, + 5, + 14, + 42, + 132, + 429, + 1430, + 4862, + 16796, + 58786, + 208012, + 742900, + 2674440, + 9694845, + 35357670, + 129644790, + 477638700, + 1767263190, + ] + + @pytest.mark.timeout(120) def test_123_with_db(): searcher = TileScope("123", all_the_strategies_verify_database) @@ -118,16 +196,136 @@ def test_123_with_db(): assert isinstance(spec, CombinatorialSpecification) -@pytest.mark.xfail @pytest.mark.timeout(20) def test_1342_1423(): - point_placements_component_fusion = point_placements.make_fusion( - component=True, tracked=False + point_placements_component_fusion = point_placements.make_fusion(tracked=True) + searcher = TrackedSearcher( + "1342_1423", point_placements_component_fusion, max_parameters=1 ) - searcher = TileScope("1342_1423", point_placements_component_fusion) spec = searcher.auto_search(smallest=True) - assert spec.number_of_rules() == 9 - assert isinstance(spec, CombinatorialSpecification) + # TODO: change to 20 when fixed param verification + assert [spec.count_objects_of_size(i) for i in range(8)] == [ + 1, + 1, + 2, + 6, + 22, + 90, + 394, + 1806, + # 8558, + # 41586, + # 206098, + # 1037718, + # 5293446, + # 27297738, + # 142078746, + # 745387038, + # 3937603038, + # 20927156706, + # 111818026018, + # 600318853926, + ] + + +@pytest.mark.timeout(20) +def test_3142_2413(): + point_placements_component_fusion = point_placements.make_fusion(tracked=True) + searcher = TrackedSearcher( + "3142_2413", point_placements_component_fusion, max_parameters=1 + ) + spec = searcher.auto_search(smallest=True) + # TODO: change to 20 when fixed param verification + assert [spec.count_objects_of_size(i) for i in range(8)] == [ + 1, + 1, + 2, + 6, + 22, + 90, + 394, + 1806, + # 8558, + # 41586, + # 206098, + # 1037718, + # 5293446, + # 27297738, + # 142078746, + # 745387038, + # 3937603038, + # 20927156706, + # 111818026018, + # 600318853926, + ] + + +@pytest.mark.timeout(20) +def test_1234(): + pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) + pack.expansion_strats[0][0].dirs = (3,) + searcher = TrackedSearcher( + "1234", + pack, + max_parameters=2, + ) + spec = searcher.auto_search() + assert [spec.count_objects_of_size(i) for i in range(20)] == [ + 1, + 1, + 2, + 6, + 23, + 103, + 513, + 2761, + 15767, + 94359, + 586590, + 3763290, + 24792705, + 167078577, + 1148208090, + 8026793118, + 56963722223, + 409687815151, + 2981863943718, + 21937062144834, + ] + + +@pytest.mark.timeout(20) +def test_1243(): + pack = TileScopePack.row_and_col_placements(row_only=True).make_fusion(tracked=True) + pack.expansion_strats[0][0].dirs = (3,) + searcher = TrackedSearcher( + "1243", + pack, + max_parameters=2, + ) + spec = searcher.auto_search() + assert [spec.count_objects_of_size(i) for i in range(20)] == [ + 1, + 1, + 2, + 6, + 23, + 103, + 513, + 2761, + 15767, + 94359, + 586590, + 3763290, + 24792705, + 167078577, + 1148208090, + 8026793118, + 56963722223, + 409687815151, + 2981863943718, + 21937062144834, + ] @pytest.mark.timeout(60) @@ -319,8 +517,7 @@ def test_expansion(): ] -@pytest.mark.xfail -@pytest.mark.timeout(30) +@pytest.mark.skip def test_domino(): domino = Tiling( obstructions=[ @@ -329,11 +526,10 @@ def test_domino(): GriddedPerm((0, 2, 1, 3), [(0, 0), (0, 1), (0, 0), (0, 1)]), ] ) - tilescope = TileScope( + tilescope = TrackedSearcher( domino, - TileScopePack.row_and_col_placements().make_fusion( - tracked=True, component=True - ), + TileScopePack.row_and_col_placements(col_only=True).make_fusion(tracked=True), + max_parameters=1, ) spec = tilescope.auto_search() assert isinstance(spec, CombinatorialSpecification) @@ -398,7 +594,8 @@ def forest_expansion(): ] -@pytest.mark.skip(reason="seems to run forever") +@pytest.mark.xfail +@pytest.mark.timeout(20) def test_guided_searcher(): tilescope = TileScope( "123", TileScopePack.point_placements().make_fusion(tracked=False)