diff --git a/mapplings/algorithms/param_redundancy.py b/mapplings/algorithms/param_redundancy.py new file mode 100644 index 0000000..3699a5d --- /dev/null +++ b/mapplings/algorithms/param_redundancy.py @@ -0,0 +1,143 @@ +from typing import Iterable +from itertools import chain, combinations, product +from gridded_cayley_permutations import Tiling, RowColMap +from gridded_cayley_permutations.simplify_obstructions_and_requirements import ( + SimplifyObstructionsAndRequirements as Simplify, +) + +from mapplings import ParameterList, Parameter + + +class CompareParameters: + """Used to check for redundant parameters""" + + def __init__(self, params: Iterable[Parameter]): + self.params = ParameterList(params) + self.redundant = set[Parameter]() + self.find_redundant() + + def __call__(self) -> ParameterList: + return ParameterList( + param for param in self.params if param not in self.redundant + ) + + @staticmethod + def all_maps( + param1: Parameter, param2: Parameter + ) -> tuple[tuple[RowColMap, ...], tuple[RowColMap, ...]]: + """Every way to map from one param to the other""" + preimages1, preimages2 = param1.map.preimage_map(), param2.map.preimage_map() + if param1.positive_cells(): + positive1 = tuple(map(set[int], zip(*param1.positive_cells()))) + else: + positive1 = (set[int](), set[int]()) + if param2.positive_cells(): + positive2 = tuple(map(set[int], zip(*param2.positive_cells()))) + else: + positive2 = (set[int](), set[int]()) + + def make_maps( + row_maps: bool, + ) -> tuple[tuple[dict[int, int], ...], tuple[dict[int, int], ...]]: + all_sections1, all_sections2 = ( + list[set[tuple[tuple[int, int], ...]]](), + list[set[tuple[tuple[int, int], ...]]](), + ) + for image in set(preimages1[row_maps].keys()) & set( + preimages2[row_maps].keys() + ): + check_positive = ( + set(preimages1[row_maps][image]) & positive1[row_maps], + set(preimages2[row_maps][image]) & positive2[row_maps], + ) + section_maps1, section_maps2 = ( + set[tuple[tuple[int, int], ...]](), + set[tuple[tuple[int, int], ...]](), + ) + if len(preimages1[row_maps][image]) > len(preimages2[row_maps][image]): + for indices in combinations( + preimages1[row_maps][image], len(preimages2[row_maps][image]) + ): + + if not check_positive[0].issubset(indices): + continue + section_maps1.add( + tuple(zip(indices, preimages2[row_maps][image])) + ) + section_maps2.add( + tuple(zip(preimages2[row_maps][image], indices)) + ) + else: + for indices in combinations( + preimages2[row_maps][image], len(preimages1[row_maps][image]) + ): + if not check_positive[1].issubset(indices): + continue + section_maps2.add( + tuple(zip(indices, preimages1[row_maps][image])) + ) + section_maps1.add( + tuple(zip(preimages1[row_maps][image], indices)) + ) + all_sections1.append(section_maps1) + all_sections2.append(section_maps2) + return tuple( + dict(chain(*tuples)) for tuples in product(*all_sections1) + ), tuple(dict(chain(*tuples)) for tuples in product(*all_sections2)) + + col_maps = make_maps(False) + row_maps = make_maps(True) + return tuple( + RowColMap(*maps) for maps in product(col_maps[0], row_maps[0]) + ), tuple(RowColMap(*maps) for maps in product(col_maps[1], row_maps[1])) + + @staticmethod + def redundancy_check(param1: Parameter, param2: Parameter): + """Returns true if param1 implies param2""" + positive_images1 = { + (param1.col_map[x], param1.row_map[y]) for x, y in param1.positive_cells() + } + positive_images2 = { + (param2.col_map[x], param2.row_map[y]) for x, y in param2.positive_cells() + } + intersection = param1.image_cells() & param2.image_cells() + if not ( + positive_images1.issubset(intersection) + and positive_images2.issubset(intersection) + ): + return False + maps = CompareParameters.all_maps(param1, param2)[0] + req_free2 = Tiling(param2.obstructions, [], param2.dimensions) + + def _check(temp_map: RowColMap) -> bool: + temp_param = Parameter(param1.ghost, temp_map) + if any( + req_free2.gcp_in_tiling(temp_param.map.map_gridded_cperm(ob)) + for ob in temp_param.obstructions + ): + print("NO", temp_param.map) + return False + for req_list in temp_param.requirements: + if not Simplify.requirement_implied_by_some_requirement( + temp_param.map.map_gridded_cperms(req_list), param2.requirements + ): + print("NO", temp_param.map) + return False + print("Yes?", temp_param.map) + return True + + for temp_map in maps: + if _check(temp_map): + return True + return False + + def find_redundant(self) -> None: + """Finds all redundant parameters in the list""" + for p1, p2 in combinations(self.params, 2): + if {p1, p2} & self.redundant: + continue + if self.redundancy_check(p1, p2): + self.redundant.add(p1) + continue + if self.redundancy_check(p2, p1): + self.redundant.add(p2) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 9f4126d..3854564 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -493,9 +493,7 @@ def status_update(cls) -> str: "right", "right", ) - logs = list( - log for log in cls.all_loggers if log.log_level > 0 and log.runs > 0 - ) + logs = list(log for log in cls.all_loggers if log.log_level > 0) if not logs: return f"Logging dissabled for all {cls.__name__} cleaners.\n" logs.sort(key=lambda log: log.total_times, reverse=True) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index a255303..6db4bf0 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -15,6 +15,7 @@ from .cleaner import GenericCleaner, Register, CleanerLog from .parameter_cleaner import ParamCleaner + default_param_cleaner = ParamCleaner.make_full_cleaner("Param Default Cleaner") @@ -226,10 +227,9 @@ def remove_empty_rows_and_cols(mappling: MappedTiling) -> MappedTiling: @reg(12) def simple_reduce_redundant_parameters(mappling: MappedTiling) -> MappedTiling: """Removes any parameter implied by another with a basic check""" - new_avoiders = mappling.avoiding_parameters.simple_remove_redundant(True) + new_avoiders = mappling.avoiding_parameters.remove_redundant() new_containers = [ - c_list.simple_remove_redundant() - for c_list in mappling.containing_parameters + c_list.remove_redundant() for c_list in mappling.containing_parameters ] return MappedTiling( mappling.tiling, diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index d92359f..6e2fedc 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -2,6 +2,7 @@ from typing import Iterator, Iterable from itertools import chain +from cayley_permutations import CayleyPermutation from gridded_cayley_permutations.row_col_map import RowColMap from gridded_cayley_permutations.unplacement import PartialUnplacement from gridded_cayley_permutations import Tiling @@ -22,7 +23,7 @@ class ParamCleaner(GenericCleaner[Parameter]): run_on_enumerators=True, ) global_tracker = CleanerLog[Parameter]( - reg.registered_functions, name="Global Tracker" + reg.registered_functions, log_level=2,name="Global Tracker" ) all_loggers = {global_tracker} @@ -30,7 +31,7 @@ class ParamCleaner(GenericCleaner[Parameter]): # Final Methods @staticmethod - @reg(3, run_on_enumerators=False) + @reg(5, run_on_enumerators=False) def reduce_by_fusion(param: Parameter) -> Parameter: """Fuses valid rows and columns""" deleted_cols, deleted_rows = set( @@ -76,7 +77,7 @@ def reduce_empty_rows_and_cols(param: Parameter) -> Parameter: def remove_blank_rows_and_cols(param: Parameter) -> Parameter: """Deletes all rows and cols which have no obs or reqs""" - blank = tuple(map(set[int], param.blank_and_near_blank())) + blank = tuple(map(set[int], param.find_blank_columns_and_rows())) if not any(blank): return param col_preimages, row_preimages = param.map.preimage_map() @@ -116,7 +117,7 @@ def to_remove( return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod - @reg(2, run_on_enumerators=False) + @reg(4, run_on_enumerators=False) def unplace_points(param: Parameter) -> Parameter: """Unplaces all possible points in the parameter""" algo = PartialUnplacement(param.ghost) @@ -147,6 +148,52 @@ def unplace_points(param: Parameter) -> Parameter: } return Parameter(new_ghost, RowColMap(new_col_map, new_row_map)) + @staticmethod + @reg(2, run_on_enumerators=False) + def insert_blank(param: Parameter) -> Parameter: + """Inserts a blank col/row in between descents/ascents wherever possible""" + if not param.requirements: + return param + to_insert = [set[int](), set[int]()] + maps = param.col_map, param.row_map + blank = tuple(map(set[int], param.find_blank_columns_and_rows())) + point_indices = param.point_cols, param.point_rows + + def validate(index1: int, index2: int, check_rows: bool) -> bool: + indeices = {index1, index2} + if maps[check_rows][index1] != maps[check_rows][index2]: + return False + if not indeices.issubset(point_indices[check_rows]): + return False + if {index1 - 1, index2 + 1} & blank[check_rows]: + return True + return False + + for req_list in param.requirements: + if not len(req_list) == 1: + continue + req = req_list[0] + if req.pattern not in ( + CayleyPermutation((0, 1)), + CayleyPermutation((1, 0)), + ): + continue + cell1, cell2 = req.positions + if (cell1[0] != cell2[0]) and (cell1[1] != cell2[1]): + continue + if cell1[0] + 1 == cell2[0]: + if validate(cell1[0], cell2[0], False): + to_insert[0].add(cell1[0]) + continue + idx1, idx2 = sorted([cell1[1], cell2[1]]) + if idx1 + 1 == idx2: + if validate(idx1, idx2, True): + to_insert[1].add(idx1) + if not any(to_insert): + return param + + return param.insert_cols_and_rows(*to_insert) + # Internal Methods @staticmethod diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 292b457..578cd25 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -50,6 +50,29 @@ def injective_cells(self) -> set[Cell]: ) return set(product(inj_cols, inj_rows)) + def single_value_cells(self) -> set[Cell]: + """Returns the set of cells with at most one value""" + cells = set[Cell]() + for cell in self.active_cells: + if ( + GriddedCayleyPerm((0, 1), (cell, cell)) in self.obstructions + and GriddedCayleyPerm((1, 0), (cell, cell)) in self.obstructions + ): + cells.add(cell) + return cells + + def single_position_cells(self) -> set[Cell]: + """Returns the set of cells with at most one position""" + cells = set[Cell]() + for cell in self.active_cells: + if ( + GriddedCayleyPerm((0, 1), (cell, cell)) in self.obstructions + and GriddedCayleyPerm((1, 0), (cell, cell)) in self.obstructions + and GriddedCayleyPerm((0, 0), (cell, cell)) in self.obstructions + ): + cells.add(cell) + return cells + def preimage_of_gcp(self, gcperm: GriddedCayleyPerm) -> Iterator[GriddedCayleyPerm]: """Returns the preimage of a gridded cayley permutation""" for gcp in self.map.preimage_of_gridded_cperm(gcperm): @@ -119,6 +142,32 @@ def update_active_cells(self, tiling: Tiling) -> "Parameter": temp.active_cells = self.active_cells return temp + def requirement_cells(self) -> set[Cell]: + """Returns every cell that contains a requirement""" + if not self.requirements: + return set[Cell]() + return set(chain(*(set(req.positions) for req in chain(*self.requirements)))) + + def blank_and_near_blank(self) -> tuple[tuple[int, ...], tuple[int, ...]]: + """Finds blank rows and cols allowing point row/col intersection""" + if self.dimensions == (0, 0): + return tuple(), tuple() + if not self.obstructions and not self.requirements: + return tuple(range(self.dimensions[0])), tuple(range(self.dimensions[1])) + req_cells = tuple( + chain(*(set(req.positions) for req in chain(*self.requirements))) + ) + check_cells = ( + cell + for cell in self.not_blank_cells() + if cell in req_cells + or (cell[0] not in self.point_cols and cell[1] not in self.point_rows) + ) + not_blank_cols, not_blank_rows = zip(*check_cells) + blank_cols = tuple(set(range(self.dimensions[0])) - set(not_blank_cols)) + blank_rows = tuple(set(range(self.dimensions[1])) - set(not_blank_rows)) + return blank_cols, blank_rows + def find_blank_columns_and_rows_in_param( self, tiling: Tiling ) -> tuple[list[int], list[int]]: @@ -241,12 +290,66 @@ def delete_preimage_of_rows_and_columns( new_row_map[key] = temp_param.row_map[key] = value - adjust return Parameter(temp_param.ghost, RowColMap(new_col_map, new_row_map)) + def insert_cols_and_rows( + self, cols: Iterable[int], rows: Iterable[int] + ) -> "Parameter": + """Inserts a blank col or col at each index. + New col/row gets map data from the col/row at the given index.""" + col_adjust = { + i: i + sum((j < i for j in cols)) for i in range(self.dimensions[0]) + } + row_adjust = { + i: i + sum((j < i for j in rows)) for i in range(self.dimensions[1]) + } + adjust = RowColMap(col_adjust, row_adjust) + new_obs = adjust.map_gridded_cperms(self.obstructions) + new_reqs = adjust.map_requirements(self.requirements) + new_col_map = dict[int, int]() + new_row_map = dict[int, int]() + new_dimensions = ( + self.dimensions[0] + len(tuple(cols)), + self.dimensions[1] + len(tuple(rows)), + ) + tweak = 0 + + for i in range(new_dimensions[0]): + if i - tweak - 1 in cols: + new_col_map[i] = new_col_map[i - 1] + tweak += 1 + else: + new_col_map[i] = self.col_map[i - tweak] + tweak = 0 + for i in range(new_dimensions[1]): + if i - tweak - 1 in rows: + new_row_map[i] = new_row_map[i - 1] + tweak += 1 + else: + new_row_map[i] = self.row_map[i - tweak] + return Parameter( + Tiling( + new_obs, + new_reqs, + new_dimensions, + False, + ), + RowColMap(new_col_map, new_row_map), + ) + def sub_parameter(self, cells: Iterable[Cell]) -> "Parameter": """Returns the parameter containing only the specified cells""" cols, rows = zip(*cells) cols_to_delete = {i for i in range(self.dimensions[0]) if i not in cols} rows_to_delete = {i for i in range(self.dimensions[1]) if i not in rows} - return self.delete_rows_and_columns(cols_to_delete, rows_to_delete) + temp = Tiling(self.obstructions, [], self.dimensions) + for req_list in self.requirements: + new_req_list = set[GriddedCayleyPerm]() + for req in req_list: + if any(pos in cells for pos in req.positions): + new_req_list.add(req.sub_gridded_cayley_perm(cells)) + if len(new_req_list) == len(req_list): + temp = temp.add_requirement_list(new_req_list) + new_param = Parameter(temp, self.map) + return new_param.delete_rows_and_columns(cols_to_delete, rows_to_delete) def factor(self) -> Iterator["Parameter"]: """Factors the ghost and combines factors with overlapping images.""" diff --git a/mapplings/parameter_list.py b/mapplings/parameter_list.py index 61770be..0ae84c6 100644 --- a/mapplings/parameter_list.py +++ b/mapplings/parameter_list.py @@ -6,10 +6,14 @@ TypeVar, TypeVarTuple, Union, + Iterable, ) -from itertools import chain, combinations +from itertools import chain, combinations, product -from gridded_cayley_permutations import Tiling +from gridded_cayley_permutations import Tiling, RowColMap +from gridded_cayley_permutations.simplify_obstructions_and_requirements import ( + SimplifyObstructionsAndRequirements as Simplify, +) from .parameter import Parameter @@ -72,22 +76,69 @@ def remove_empty(self) -> "ParameterList": def simple_remove_redundant(self, reverse: bool = False) -> "ParameterList": """Removes any parameter implied by another through a basic check""" exclude = set[Parameter]() + + def match_reverse(smaller: Parameter, bigger: Parameter) -> None: + if reverse: + exclude.add(smaller) + else: + exclude.add(bigger) + + def compare(smaller: Parameter, bigger: Parameter) -> bool: + temp_bigger = bigger.sub_parameter( + bigger.map.preimage_of_cells(smaller.image_cells()) + ) + if len(bigger.requirements) != len(temp_bigger.requirements): + return False + + if smaller.map != temp_bigger.map: + return False + if not reverse: + if any(smaller.gcp_in_tiling(ob) for ob in temp_bigger.obstructions): + return False + for req_list in smaller.requirements: + if not Simplify.requirement_implied_by_some_requirement( + req_list, temp_bigger.requirements + ): + return False + return True + if any(temp_bigger.gcp_in_tiling(ob) for ob in smaller.obstructions): + return False + for req_list in smaller.requirements: + # any req list that doesnt have any req implied by all reqs of a small req list + if all( + all( + any(big_req.avoids([req]) for big_req in big_list) + for big_list in temp_bigger.requirements + ) + for req in req_list + ): + return False + return True + for param0, param1 in combinations(self, 2): if {param0, param1} & exclude: continue - image_cells = param0.image_cells() - if not image_cells.issubset(param1.image_cells()): - continue - temp_param = param1.sub_parameter(param1.map.preimage_of_cells(image_cells)) - if param0.map != temp_param.map: - continue - if param0.ghost.is_subset(temp_param.ghost): - if reverse: - exclude.add(param0) - else: - exclude.add(param1) + image_cells = param0.image_cells(), param1.image_cells() + if image_cells[0] == image_cells[1]: + if compare(param0, param1): + match_reverse(param0, param1) + elif compare(param1, param0): + match_reverse(param1, param0) + + elif image_cells[0].issubset(image_cells[1]): + if compare(param0, param1): + match_reverse(param0, param1) + + elif image_cells[1].issubset(image_cells[0]): + if compare(param1, param0): + match_reverse(param1, param0) + return ParameterList(param for param in self if param not in exclude) + def remove_redundant(self) -> "ParameterList": + """Returns self with redundant parameters removed""" + return CompareParameters(self)() + def to_html(self) -> str: """Returns a html of all parameters in self seperated by a line""" return "
".join((param.to_html_representation() for param in sorted(self))) @@ -127,3 +178,174 @@ def __lt__(self, other: object): if isinstance(other, ParameterList): return tuple(sorted(self)) < tuple(sorted(other)) return NotImplemented + + +class CompareParameters: + """Used to check for redundant parameters""" + + def __init__(self, params: Iterable[Parameter]): + self.params = ParameterList(params) + self.redundant = set[Parameter]() + self.find_redundant() + + def __call__(self) -> ParameterList: + if len(self.params) < 2: + return self.params + return ParameterList( + param for param in self.params if param not in self.redundant + ) + + @staticmethod + def all_maps( + param1: Parameter, param2: Parameter + ) -> tuple[tuple[RowColMap, ...], tuple[RowColMap, ...]]: + """Every way to map from one param to the other""" + preimages1, preimages2 = param1.map.preimage_map(), param2.map.preimage_map() + if param1.positive_cells(): + positive1 = tuple(map(set[int], zip(*param1.positive_cells()))) + else: + positive1 = (set[int](), set[int]()) + if param2.positive_cells(): + positive2 = tuple(map(set[int], zip(*param2.positive_cells()))) + else: + positive2 = (set[int](), set[int]()) + + point_indices1 = param1.point_cols, param1.point_rows + point_indices2 = param2.point_cols, param2.point_rows + + def make_maps( + row_maps: bool, + ) -> tuple[tuple[dict[int, int], ...], tuple[dict[int, int], ...]]: + all_sections1, all_sections2 = ( + list[set[tuple[tuple[int, int], ...]]](), + list[set[tuple[tuple[int, int], ...]]](), + ) + for image in set(preimages1[row_maps].keys()) & set( + preimages2[row_maps].keys() + ): + maxes = max(preimages1[row_maps][image]), max( + preimages2[row_maps][image] + ) + mins = min(preimages1[row_maps][image]), min( + preimages2[row_maps][image] + ) + force_extremal = ( + [i in point_indices1[row_maps] for i in (mins[0], maxes[0])], + [i in point_indices2[row_maps] for i in (maxes[1], mins[1])], + ) + check_positive = ( + set(preimages1[row_maps][image]) & positive1[row_maps], + set(preimages2[row_maps][image]) & positive2[row_maps], + ) + section_maps1, section_maps2 = ( + set[tuple[tuple[int, int], ...]](), + set[tuple[tuple[int, int], ...]](), + ) + if len(preimages1[row_maps][image]) > len(preimages2[row_maps][image]): + for indices in combinations( + preimages1[row_maps][image], len(preimages2[row_maps][image]) + ): + if force_extremal[1][0] and mins[0] not in indices: + continue + if force_extremal[1][1] and maxes[0] not in indices: + continue + + if not check_positive[0].issubset(indices): + continue + section_maps1.add( + tuple(zip(indices, preimages2[row_maps][image])) + ) + section_maps2.add( + tuple(zip(preimages2[row_maps][image], indices)) + ) + else: + for indices in combinations( + preimages2[row_maps][image], len(preimages1[row_maps][image]) + ): + if force_extremal[0][0] and mins[1] not in indices: + continue + if force_extremal[0][1] and maxes[1] not in indices: + continue + if not check_positive[1].issubset(indices): + continue + section_maps2.add( + tuple(zip(indices, preimages1[row_maps][image])) + ) + section_maps1.add( + tuple(zip(preimages1[row_maps][image], indices)) + ) + all_sections1.append(section_maps1) + all_sections2.append(section_maps2) + return tuple( + dict(chain(*tuples)) for tuples in product(*all_sections1) + ), tuple(dict(chain(*tuples)) for tuples in product(*all_sections2)) + + col_maps = make_maps(False) + row_maps = make_maps(True) + return tuple( + RowColMap(*maps) for maps in product(col_maps[0], row_maps[0]) + ), tuple(RowColMap(*maps) for maps in product(col_maps[1], row_maps[1])) + + @staticmethod + def redundancy_check(param1: Parameter, param2: Parameter): + """Returns true if param1 implies param2""" + positive_images1 = { + (param1.col_map[x], param1.row_map[y]) for x, y in param1.positive_cells() + } + positive_images2 = { + (param2.col_map[x], param2.row_map[y]) for x, y in param2.positive_cells() + } + intersection = param1.image_cells() & param2.image_cells() + if not ( + positive_images1.issubset(intersection) + and positive_images2.issubset(intersection) + ): + return False + maps = CompareParameters.all_maps(param1, param2)[0] + req_free2 = Tiling(param2.obstructions, [], param2.dimensions) + + def _check(temp_map: RowColMap) -> bool: + temp_param = Parameter(param1.ghost, temp_map) + backwards_map = RowColMap( + {val: key for key, val in temp_map.col_map.items()}, + {val: key for key, val in temp_map.row_map.items()}, + ) + if any( + req_free2.gcp_in_tiling( + temp_param.map.map_gridded_cperm( + ob.sub_gridded_cayley_perm(backwards_map.image_cells) + ) + ) + for ob in temp_param.obstructions + ): + return False + for req_list in temp_param.requirements: + temp_req_list = [ + req + for req in req_list + if set(req.positions).issubset(backwards_map.image_cells) + ] + if not temp_req_list: + return False + if not Simplify.requirement_implied_by_some_requirement( + temp_param.map.map_gridded_cperms(temp_req_list), + param2.requirements, + ): + return False + return True + + for temp_map in maps: + if _check(temp_map): + return True + return False + + def find_redundant(self) -> None: + """Finds all redundant parameters in the list""" + for p1, p2 in combinations(self.params, 2): + if {p1, p2} & self.redundant: + continue + if self.redundancy_check(p1, p2): + self.redundant.add(p1) + continue + if self.redundancy_check(p2, p1): + self.redundant.add(p2) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 1cfcaf6..1f1018e 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -41,8 +41,6 @@ ) from mapplings.cleaners import MTCleaner, ParamCleaner - -MTCleaner.global_log_toggle(1) temp = CombinatorialSpecificationSearcher.status @@ -294,7 +292,7 @@ class MapplingFactorStrategy(FactorStrategy): A strategy for finding factors in a mapped tiling. """ - cleaner = MTCleaner.make_full_cleaner("Factoring Cleaner") + cleaner = MTCleaner([], "Factoring Cleaner") def decomposition_function(self, comb_class) -> tuple[MappedTiling, ...]: factors = Factor(comb_class).find_factors() @@ -348,6 +346,8 @@ class MapplingLessThanRowColSeparationStrategy(LessThanRowColSeparationStrategy) def decomposition_function(self, comb_class): algo = LTRowColSeparationMT(comb_class) + if algo.separation.row_col_map.is_identity(): + raise StrategyDoesNotApply return tuple(map(self.__class__.cleaner, algo.separate())) @@ -360,4 +360,6 @@ class MapplingLessThanOrEqualRowColSeparationStrategy( def decomposition_function(self, comb_class): algo = LTORERowColSeparationMT(comb_class) + if algo.separation.row_col_map.is_identity(): + raise StrategyDoesNotApply return tuple(map(self.__class__.cleaner, algo.separate()))