From f5036d7b9c57b6e73582b3a51eebe35ccfa9ba33 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:21:56 +0000 Subject: [PATCH 01/24] Unplacement Changes ParamCleaner unplacement now does partial unplacement --- mapplings/cleaners/parameter_cleaner.py | 55 ++++++++++++------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 18132a2..7f877bd 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -2,7 +2,7 @@ from typing import Iterable from gridded_cayley_permutations.row_col_map import RowColMap -from gridded_cayley_permutations.unplacement import PointUnplacement +from gridded_cayley_permutations.unplacement import PartialUnplacement from gridded_cayley_permutations import Tiling from mapplings import Parameter @@ -102,34 +102,31 @@ def check_for_blank(columns: Iterable[int], image: int, check_rows: bool): @reg(2, run_on_enumerators=False) def unplace_points(param: Parameter) -> Parameter: """Unplaces all possible points in the parameter""" - found = True - new_param = Parameter(param.ghost, param.map) - while found: - found = False - for cell in new_param.point_cells(): - algo = PointUnplacement(new_param.ghost, cell) - if not algo.cell_in_valid_region(): - continue - if ( - not new_param.col_map[cell[0] - 1] - == new_param.col_map[cell[0]] - == new_param.col_map[cell[0] + 1] - ): - continue - if ( - not new_param.row_map[cell[1] - 1] - == new_param.row_map[cell[1]] - == new_param.row_map[cell[1] + 1] - ): - continue - check_reqs = algo.intersecting_req_list() - if PointUnplacement(new_param.ghost, cell).point_can_be_unplaced( - check_reqs - ): - new_param = new_param.unplace_point(cell) - found = True - break - return new_param + algo = PartialUnplacement(param.ghost) + points = param.point_cells() + cells, cols, rows = set[tuple[int, int]](), set[int](), set[int]() + for cell in points: + valid = algo.cell_in_valid_region(cell) + if valid[0] and param.col_map[cell[0] - 1] == param.col_map[cell[0] + 1]: + cells.add(cell) + cols.add(cell[0]) + if valid[1] and param.row_map[cell[1] - 1] == param.row_map[cell[1] + 1]: + cells.add(cell) + rows.add(cell[1]) + unplace_cols, unplace_rows = algo.fusable_check(cells, cols, rows) + new_ghost = algo.unplace(unplace_cols, unplace_rows) + col_preimages, row_preimages = algo.adjustment_map( + unplace_cols, unplace_rows + ).preimage_map() + new_col_map = { + i: param.col_map[col_preimages[i][0]] + for i in range(new_ghost.dimensions[0]) + } + new_row_map = { + i: param.row_map[row_preimages[i][0]] + for i in range(new_ghost.dimensions[1]) + } + return Parameter(new_ghost, RowColMap(new_col_map, new_row_map)) # Internal Methods From 0eedd42a10238f92a12f83217f47d00da1274e3c Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:57:37 +0000 Subject: [PATCH 02/24] Fixes Changed Param Counting. Added conditions to blank row/col removal. Made the ParamCleaner log work. --- mapplings/cleaners/mappling_cleaner.py | 15 ++++------- mapplings/cleaners/parameter_cleaner.py | 34 +++++++++++++++++++++++++ mapplings/parameter.py | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 6a5556a..88a01fd 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -50,13 +50,15 @@ def _clean_parameters(mappling: MappedTiling) -> MappedTiling: if getattr(func, "run_on_avoiders"): new_avoiders = ParameterList( param.update_active_cells(mappling.tiling) - for param in new_avoiders.apply_to_all(func) + for param in new_avoiders.apply_to_all( + param_cleaner.logger(func) + ) ) if getattr(func, "run_on_containers"): new_containers = [ ParameterList( param.update_active_cells(mappling.tiling) - for param in c_list.apply_to_all(func) + for param in c_list.apply_to_all(param_cleaner.logger(func)) ) for c_list in new_containers ] @@ -64,7 +66,7 @@ def _clean_parameters(mappling: MappedTiling) -> MappedTiling: new_enumerators = [ ParameterList( param.update_active_cells(mappling.tiling) - for param in e_list.apply_to_all(func) + for param in e_list.apply_to_all(param_cleaner.logger(func)) ) for e_list in new_enumerators ] @@ -277,16 +279,9 @@ def forward_map_parameter_gcps_from_avoiders( avoiders, containers, enumerators = mappling.ace_parameters() new_avoiders = [] for avoider in avoiders: - empty_cells = ( - set(product(range(avoider.dimensions[0]), range(avoider.dimensions[1]))) - - avoider.active_cells - ) injective_cells = avoider.active_cells & ( avoider.injective_cells() - avoider.point_cells() ) - if injective_cells == avoider.active_cells and empty_cells: - new_avoiders.append(avoider) - continue new_reqs = [] for req_list in avoider.requirements: req_list_positions = set( diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 7f877bd..d69324d 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -23,6 +23,7 @@ class ParamCleaner(GenericCleaner[Parameter]): global_tracker = CleanerLog[Parameter]( reg.registered_functions, name="Global Tracker" ) + all_loggers = {global_tracker} # Final Methods @@ -57,6 +58,7 @@ def reduce_empty_rows_and_cols(param: Parameter) -> Parameter: @reg(1, run_on_enumerators=False) def remove_blank_rows_and_cols(param: Parameter) -> Parameter: """Deletes all rows and cols which have no obs or reqs""" + columns, rows = param.find_blank_columns_and_rows() cols_to_remove, rows_to_remove = set(), set() if param.positive_cells(): @@ -96,6 +98,36 @@ def check_for_blank(columns: Iterable[int], image: int, check_rows: bool): or len(rows_to_remove) == param.dimensions[1] ): return Parameter(Tiling([], [], (1, 1)), RowColMap({0: 0}, {0: 0})) + + if param.point_cells(): + cols_with_point, rows_with_point = map(set, zip(*param.point_cells())) + temp_cols, temp_rows = set(), set() + for col in cols_to_remove: + if col - 1 in cols_with_point: + if col + 1 in cols_to_remove: + temp_cols.add(col + 1) + else: + temp_cols.add(col) + cols_to_remove = set() + for col in temp_cols: + if col + 1 in cols_with_point: + if col - 1 in temp_cols: + cols_to_remove.add(col - 1) + else: + cols_to_remove.add(col) + for row in rows_to_remove: + if row - 1 in rows_with_point: + if row + 1 in rows_to_remove: + temp_rows.add(row + 1) + else: + temp_rows.add(row) + rows_to_remove = set() + for row in temp_rows: + if row + 1 in rows_with_point: + if row - 1 in temp_rows: + rows_to_remove.add(row - 1) + else: + rows_to_remove.add(row) return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod @@ -114,6 +146,8 @@ def unplace_points(param: Parameter) -> Parameter: cells.add(cell) rows.add(cell[1]) unplace_cols, unplace_rows = algo.fusable_check(cells, cols, rows) + if not (unplace_cols or unplace_rows): + return param new_ghost = algo.unplace(unplace_cols, unplace_rows) col_preimages, row_preimages = algo.adjustment_map( unplace_cols, unplace_rows diff --git a/mapplings/parameter.py b/mapplings/parameter.py index eca77ab..008513b 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -59,7 +59,7 @@ def gcp_has_preimage(self, gcp: GriddedCayleyPerm) -> bool: """Determines if the sub-gridding of the gcp that lives in the image region has a preimage on the ghost""" sub_gridding = gcp.sub_gridded_cayley_perm(self.image_cells()) - if not sub_gridding.positions and not self.positive_cells(): + if not sub_gridding.positions and not self.requirements: return True for preimage in self.map.preimage_of_gridded_cperm(sub_gridding): if self.gcp_in_tiling(preimage): From 6282067f0bd675026a22438089b413995acb6f8b Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:27:50 +0000 Subject: [PATCH 03/24] Ugly Fixes I don't like this code, but its for hunting down what's actually broken. --- mapplings/cleaners/mappling_cleaner.py | 36 +++++++-- mapplings/cleaners/parameter_cleaner.py | 102 ++++++++++++++++-------- mapplings/parameter.py | 55 +++++++++++++ mapplings/parameter_list.py | 7 +- 4 files changed, 157 insertions(+), 43 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 88a01fd..db3448b 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -226,7 +226,7 @@ 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() + new_avoiders = mappling.avoiding_parameters.simple_remove_redundant(True) new_containers = [ c_list.simple_remove_redundant() for c_list in mappling.containing_parameters @@ -279,10 +279,20 @@ def forward_map_parameter_gcps_from_avoiders( avoiders, containers, enumerators = mappling.ace_parameters() new_avoiders = [] for avoider in avoiders: + if not (avoider.obstructions or avoider.requirements): + new_avoiders.append(avoider) + continue + empty_cells = ( + set(product(range(avoider.dimensions[0]), range(avoider.dimensions[1]))) + - avoider.active_cells + ) injective_cells = avoider.active_cells & ( avoider.injective_cells() - avoider.point_cells() ) - new_reqs = [] + if injective_cells == avoider.active_cells and empty_cells: + new_avoiders.append(avoider) + continue + new_reqs, add_reqs = [], set[GriddedCayleyPerm]() for req_list in avoider.requirements: req_list_positions = set( chain.from_iterable((req.positions for req in req_list)) @@ -290,10 +300,15 @@ def forward_map_parameter_gcps_from_avoiders( if not req_list_positions.issubset(injective_cells): new_reqs.append(req_list) continue - new_base = new_base.add_obstructions( - avoider.map.map_gridded_cperms(req_list) - ) - new_obs = avoider.obstructions + add_reqs.update(set(req_list)) + new_base = new_base.add_obstructions( + avoider.map.map_gridded_cperms(add_reqs) + ) + new_obs = { + ob + for ob in avoider.obstructions + if not any((ob.contains_gridded_cperm(req) for req in add_reqs)) + } if new_obs or new_reqs: new_avoiders.append( Parameter( @@ -418,15 +433,22 @@ def _reduce_parameter_gcps(mappling: MappedTiling, param: Parameter) -> Paramete simplify.remove_redundant_obstructions() new_obs = [] for ob in simplify.obstructions: + if ob.pattern in (CayleyPermutation([0, 1]), CayleyPermutation([1, 0])): + if all( + position in param.single_value_cells() for position in ob.positions + ): + new_obs.append(ob) + continue if len(set(ob.positions)) == 1: cell = ob.positions[0] if ( - cell in point_cells + cell in param.single_value_cells() and (param.col_map[cell[0]], param.row_map[cell[1]]) not in mappling_point_cells ): new_obs.append(ob) continue + if any( param.map.map_gridded_cperm(ob).contains_gridded_cperm(mt_ob) for mt_ob in mappling.obstructions diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index d69324d..db15376 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -1,6 +1,7 @@ """Module with the parameter cleaner""" from typing import Iterable +from itertools import chain from gridded_cayley_permutations.row_col_map import RowColMap from gridded_cayley_permutations.unplacement import PartialUnplacement from gridded_cayley_permutations import Tiling @@ -61,24 +62,26 @@ def remove_blank_rows_and_cols(param: Parameter) -> Parameter: columns, rows = param.find_blank_columns_and_rows() cols_to_remove, rows_to_remove = set(), set() - if param.positive_cells(): - positive_cols, positive_rows = map(set, zip(*param.positive_cells())) + if param.requirements: + req_cols, req_rows = zip( + *chain(*(set(req.positions) for req in chain(*param.requirements))) + ) else: - positive_cols, positive_rows = set(), set() + req_cols, req_rows = tuple(), tuple() def check_for_blank(columns: Iterable[int], image: int, check_rows: bool): for col in columns: if check_rows: if col in rows_to_remove: break - if param.row_map[col] == image and col not in positive_rows: + if param.row_map[col] == image and col not in req_rows: rows_to_remove.add(col) else: break else: if col in cols_to_remove: break - if param.col_map[col] == image and col not in positive_cols: + if param.col_map[col] == image and col not in req_cols: cols_to_remove.add(column) else: break @@ -99,35 +102,66 @@ def check_for_blank(columns: Iterable[int], image: int, check_rows: bool): ): return Parameter(Tiling([], [], (1, 1)), RowColMap({0: 0}, {0: 0})) - if param.point_cells(): - cols_with_point, rows_with_point = map(set, zip(*param.point_cells())) - temp_cols, temp_rows = set(), set() - for col in cols_to_remove: - if col - 1 in cols_with_point: - if col + 1 in cols_to_remove: - temp_cols.add(col + 1) - else: - temp_cols.add(col) - cols_to_remove = set() - for col in temp_cols: - if col + 1 in cols_with_point: - if col - 1 in temp_cols: - cols_to_remove.add(col - 1) - else: - cols_to_remove.add(col) - for row in rows_to_remove: - if row - 1 in rows_with_point: - if row + 1 in rows_to_remove: - temp_rows.add(row + 1) - else: - temp_rows.add(row) - rows_to_remove = set() - for row in temp_rows: - if row + 1 in rows_with_point: - if row - 1 in temp_rows: - rows_to_remove.add(row - 1) - else: - rows_to_remove.add(row) + single_pos, single_val = ( + param.single_position_cells(), + param.single_value_cells(), + ) + cols_with_point, rows_with_point = set[int](), set[int]() + if single_pos: + cols_with_point, _ = zip(*single_pos) + if single_val: + _, rows_with_point = zip(*single_val) + temp_cols, temp_rows = set(), set() + for col in cols_to_remove: + if ( + col - 1 in cols_with_point + and param.col_map[col] == param.col_map[col - 1] + ): + if ( + col + 1 in cols_to_remove + and param.col_map[col] == param.col_map[col + 1] + ): + temp_cols.add(col + 1) + else: + temp_cols.add(col) + cols_to_remove = set() + for col in temp_cols: + if ( + col + 1 in cols_with_point + and param.col_map[col] == param.col_map[col + 1] + ): + if ( + col - 1 in temp_cols + and param.col_map[col] == param.col_map[col - 1] + ): + cols_to_remove.add(col - 1) + else: + cols_to_remove.add(col) + for row in rows_to_remove: + if ( + row - 1 in rows_with_point + and param.row_map[row] == param.row_map[row - 1] + ): + if ( + row + 1 in rows_to_remove + and param.row_map[row] == param.row_map[row + 1] + ): + temp_rows.add(row + 1) + else: + temp_rows.add(row) + rows_to_remove = set() + for row in temp_rows: + if ( + row + 1 in rows_with_point + and param.row_map[row] == param.row_map[row + 1] + ): + if ( + row - 1 in temp_rows + and param.row_map[row] == param.row_map[row - 1] + ): + rows_to_remove.add(row - 1) + else: + rows_to_remove.add(row) return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 008513b..3d7c467 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -12,6 +12,7 @@ from gridded_cayley_permutations.factors import Factors Cell = tuple[int, int] +Objects = defaultdict[tuple[int, ...], list[GriddedCayleyPerm]] class Parameter(Tiling): @@ -49,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): @@ -365,6 +389,37 @@ def to_html_representation(self) -> str: return "".join(result) + def objects_of_size(self, n, **parameters) -> Iterator[GriddedCayleyPerm]: + """Return gridded Cayley permutations of size n in the tiling.""" + for val in self.get_objects(n).values(): + yield from val + + def get_objects(self, n: int) -> Objects: + """Return the objects of size n in the tiling.""" + objects = defaultdict(list) + col_map = { + val: key for key, val in enumerate(sorted(set(self.col_map.values()))) + } + row_map = { + val: key for key, val in enumerate(sorted(set(self.row_map.values()))) + } + map_reduction = RowColMap( + {key: col_map[val] for key, val in self.col_map.items()}, + {key: row_map[val] for key, val in self.row_map.items()}, + ) + temp = Parameter(self.ghost, map_reduction) + base = Tiling([], [], (len(col_map), len(row_map))) + for gcp in base.objects_of_size(n): + if temp.gcp_has_preimage(gcp): + param = self.get_parameters(gcp) + objects[param].append(gcp) + return objects + + def get_parameters(self, obj: GriddedCayleyPerm) -> tuple[int, ...]: + """Parameters are not what you think!!! This is specific to + combinatorical class parameters""" + return (1,) + # dunder methods def to_jsonable(self) -> dict: diff --git a/mapplings/parameter_list.py b/mapplings/parameter_list.py index 4037c92..b3f1095 100644 --- a/mapplings/parameter_list.py +++ b/mapplings/parameter_list.py @@ -69,7 +69,7 @@ def remove_empty(self) -> "ParameterList": """Removes parameters with empty ghost""" return ParameterList(param for param in self if not param.is_empty()) - def simple_remove_redundant(self) -> "ParameterList": + def simple_remove_redundant(self, reverse: bool = False) -> "ParameterList": """Removes any parameter implied by another through a basic check""" exclude = set[Parameter]() for param0, param1 in combinations(self, 2): @@ -82,7 +82,10 @@ def simple_remove_redundant(self) -> "ParameterList": if param0.map != temp_param.map: continue if param0.ghost.is_subset(temp_param.ghost): - exclude.add(param1) + if reverse: + exclude.add(param0) + else: + exclude.add(param1) return ParameterList(param for param in self if param not in exclude) def to_html(self) -> str: From 2e70e07623e8cc828a38daee8e66d56a697f5a26 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:53:04 +0000 Subject: [PATCH 04/24] Changes From Meeting I left the motzkin playground changes because I thought they would be helpful. --- mapplings/cleaners/mappling_cleaner.py | 18 ++++++- mapplings/mapped_tiling.py | 12 +++-- mapplings/parameter.py | 4 +- mapplings/parameter_list.py | 4 +- playground/cleaner_motzkin.py | 72 +++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index db3448b..1888a77 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -4,7 +4,7 @@ from itertools import chain, product from functools import partial -from gridded_cayley_permutations import GriddedCayleyPerm, Tiling +from gridded_cayley_permutations import GriddedCayleyPerm, Tiling, RowColMap from gridded_cayley_permutations.simplify_obstructions_and_requirements import ( SimplifyObstructionsAndRequirements, ) @@ -462,7 +462,21 @@ def _reduce_parameter_gcps(mappling: MappedTiling, param: Parameter) -> Paramete req_list, simplify.requirements ) ] - new_ghost = Tiling(new_obs, new_reqs, param.dimensions, simplify=False) + final_reqs = [] + for req_list in new_reqs: + new_req_list = [ + req + for req in req_list + if all( + not param.map.map_gridded_cperm(req).contains_gridded_cperm(ob) + for ob in mappling.obstructions + ) + ] + if new_req_list: + final_reqs.append(new_req_list) + else: + return Parameter(Tiling.empty_tiling(), RowColMap({}, {})) + new_ghost = Tiling(new_obs, final_reqs, param.dimensions, simplify=False) return Parameter(new_ghost, param.map) @staticmethod diff --git a/mapplings/mapped_tiling.py b/mapplings/mapped_tiling.py index b65bb39..2a6fc93 100644 --- a/mapplings/mapped_tiling.py +++ b/mapplings/mapped_tiling.py @@ -267,13 +267,19 @@ def __str__(self) -> str: "Base tiling: \n" + str(self.tiling) + "\nAvoiding parameters:\n" - + "\n".join([str(p) for p in self.avoiding_parameters]) + + "\n".join([str(p) for p in sorted(self.avoiding_parameters)]) + "\nContaining parameters:\n" + "\nNew containing parameters list \n".join( - ["\n".join([str(p) for p in ps]) for ps in self.containing_parameters] + [ + "\n".join([str(p) for p in sorted(ps)]) + for ps in self.containing_parameters + ] ) + "\nEnumerating parameters:\n" + "\nNew enumerating parameters list\n".join( - ["\n".join([str(p) for p in ps]) for ps in self.enumerating_parameters] + [ + "\n".join([str(p) for p in sorted(ps)]) + for ps in self.enumerating_parameters + ] ) ) diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 3d7c467..02ab439 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -458,12 +458,12 @@ def __hash__(self) -> int: def __leq__(self, other: object) -> bool: if not isinstance(other, Parameter): return NotImplemented - return (self.ghost, self.map) <= (other.ghost, other.map) + return (self.map, self.ghost) <= (other.map, other.ghost) def __lt__(self, other: object) -> bool: if not isinstance(other, Parameter): return NotImplemented - return (self.ghost, self.map) < (other.ghost, other.map) + return (self.map, self.ghost) < (other.map, other.ghost) def _string_table(self) -> list[str]: """Creates a list of strings for each row of the __str__ grid""" diff --git a/mapplings/parameter_list.py b/mapplings/parameter_list.py index b3f1095..61770be 100644 --- a/mapplings/parameter_list.py +++ b/mapplings/parameter_list.py @@ -19,7 +19,7 @@ FuncTypeT = TypeVar("FuncTypeT") ArgsType = TypeVarTuple("ArgsType") -OPEN_DISPLAY = "" # Change to "open" for fully expanded html trees +OPEN_DISPLAY = "open" # Change to "open" for fully expanded html trees class ParameterList(frozenset[Parameter]): @@ -90,7 +90,7 @@ def simple_remove_redundant(self, reverse: bool = False) -> "ParameterList": 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 self)) + return "
".join((param.to_html_representation() for param in sorted(self))) def html_dropdown(self, label: str, border_color: str = "grey") -> str: """Makes a cute html dropdown for the parameter list""" diff --git a/playground/cleaner_motzkin.py b/playground/cleaner_motzkin.py index 64409b1..a72bb85 100644 --- a/playground/cleaner_motzkin.py +++ b/playground/cleaner_motzkin.py @@ -1,11 +1,21 @@ from cayley_permutations import CayleyPermutation from gridded_cayley_permutations import Tiling, GriddedCayleyPerm -from mapplings import MappedTiling, Parameter -from mapplings.cleaners import MTCleaner +from mapplings import MappedTiling, Parameter, ParameterList +from mapplings.cleaners import MTCleaner, ParamCleaner from gridded_cayley_permutations.row_col_map import RowColMap from mapplings.strategies import MappedTileScopePack from comb_spec_searcher import CombinatorialSpecificationSearcher +from mapplings.strategies.tilescope_strategies import ( + MapplingCellInsertionFactory, + MapplingPointPlacementFactory, + CleaningStrategy, + MapplingFactorStrategy, + MapplingLessThanOrEqualRowColSeparationStrategy, + MapplingLessThanRowColSeparationStrategy, +) + +cleaner = MTCleaner.make_full_cleaner() til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) ghost = til.delete_rows([4]) @@ -30,4 +40,62 @@ searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=False) spec = searcher.auto_search(status_update=10) +print(spec.count_objects_of_size(5)) spec.show() + +M0 = cleaner(mappling) +print(M0) + +print("------------------ Cell Insertion ------------------") +rule0 = list(MapplingCellInsertionFactory()(M0))[0](M0) +print(rule0.sanity_check(5)) +M1 = rule0.children[1] +print(M1) + +print("------------------ Placed Rightmost ------------------") +rule1 = list(MapplingPointPlacementFactory()(M1))[0](M1) +print(rule1.sanity_check(5)) +M2 = rule1.children[1] +M3 = cleaner(M2) +print(M3) + + +print("------------------ Factored ------------------") + +rule2 = MapplingFactorStrategy()(M3) +print(rule2.sanity_check(5)) +M4 = cleaner(rule2.children[0]) + +print(M4) +print("------------------ RC Seperation ------------------") +rule3 = MapplingLessThanRowColSeparationStrategy()(M4) +print(rule3.sanity_check(5)) +M5 = rule3.children[0] + +M6 = cleaner(M5) +print(M6) +print("------------------ Factored ------------------") +rule4 = MapplingFactorStrategy()(M6) +print(rule4.sanity_check(5)) +M7 = rule4.children[1] +print(M7) +M7 = cleaner(M7) +print("------------------ Cell Insertion ------------------") +rule5 = list(MapplingCellInsertionFactory()(M7))[0](M7) +print(rule5.sanity_check(5)) +M8 = rule5.children[1] +print(M8) + +print("------------------ Placed Top-Rightmost ------------------") +rule6 = list(MapplingPointPlacementFactory()(M8))[1](M8) +print(rule6.sanity_check(5)) +M9 = rule6.children[1] +M10 = cleaner(M9) +print(cleaner(M10)) +print("------------------ Factored ------------------") +rule7 = MapplingFactorStrategy()(M10) +print(rule7.sanity_check(5)) +M11 = rule7.children[1] +M12 = cleaner(M11) +print(M12) +print(M12 == M0) From 25b643b7e49c0edd9adc5a768aeef8c6e43b5ae3 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:58:35 +0000 Subject: [PATCH 05/24] More Fixes Small ob and reduce param gcps --- mapplings/cleaners/mappling_cleaner.py | 55 +++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 1888a77..186711e 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -243,7 +243,11 @@ def simple_reduce_redundant_parameters(mappling: MappedTiling) -> MappedTiling: def reduce_all_parameter_gcps(mappling: MappedTiling) -> MappedTiling: """Removes all obs and reqs that are implied by the base tiling from all Parameters""" param_reducer = partial(MTCleaner._reduce_parameter_gcps, mappling) - return mappling.apply_to_all_parameters(param_reducer) + avoiders, containers, enumerators = mappling.apply_to_all_parameters( + param_reducer + ).ace_parameters() + new_avoiders = ParameterList(av for av in avoiders if av.dimensions != (0, 0)) + return MappedTiling(mappling.tiling, new_avoiders, containers, enumerators) @staticmethod @reg(11) @@ -260,12 +264,50 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling: for ob in small_obs: if ob.pattern == CayleyPermutation((0, 0)): - new_mappling = new_mappling.apply_to_all_parameters( - MTCleaner._cayley_ob_adjust_param, (ob,) + new_avoiders = tuple( + avoider + for avoider in new_mappling.avoiding_parameters.apply_to_all( + MTCleaner._cayley_ob_adjust_param, (ob,) + ) + if avoider.dimensions != (0, 0) + ) + new_containers = tuple( + ParameterList( + c_list.apply_to_all(MTCleaner._cayley_ob_adjust_param, (ob,)) + ) + for c_list in mappling.containing_parameters + ) + new_enumerators = tuple( + ParameterList( + e_list.apply_to_all(MTCleaner._cayley_ob_adjust_param, (ob,)) + ) + for e_list in mappling.enumerating_parameters + ) + new_mappling = MappedTiling( + mappling.tiling, new_avoiders, new_containers, new_enumerators ) else: - new_mappling = new_mappling.apply_to_all_parameters( - MTCleaner._ob_adjust_param, (ob,) + new_avoiders = tuple( + avoider + for avoider in new_mappling.avoiding_parameters.apply_to_all( + MTCleaner._ob_adjust_param, (ob,) + ) + if avoider.dimensions != (0, 0) + ) + new_containers = tuple( + ParameterList( + c_list.apply_to_all(MTCleaner._ob_adjust_param, (ob,)) + ) + for c_list in mappling.containing_parameters + ) + new_enumerators = tuple( + ParameterList( + e_list.apply_to_all(MTCleaner._ob_adjust_param, (ob,)) + ) + for e_list in mappling.enumerating_parameters + ) + new_mappling = MappedTiling( + mappling.tiling, new_avoiders, new_containers, new_enumerators ) return new_mappling @@ -527,6 +569,9 @@ def _ob_adjust_param(param: Parameter, input_ob: GriddedCayleyPerm) -> Parameter continue if (cell[1] < point[1]) == increasing: add_obs.append(GriddedCayleyPerm((0,), [cell])) + for req_list in param.requirements: + if all(req.contains(add_obs) for req in req_list): + return Parameter(Tiling.empty_tiling(), RowColMap({}, {})) return Parameter(new_ghost.add_obstructions(add_obs), param.map) @staticmethod From 700ada12528c57effb4a14e1844494d93bda285f Mon Sep 17 00:00:00 2001 From: Abigail Ollson Date: Tue, 27 Jan 2026 08:31:49 +0000 Subject: [PATCH 06/24] hare file --- playground/hare_2_stack_sortable.py | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 playground/hare_2_stack_sortable.py diff --git a/playground/hare_2_stack_sortable.py b/playground/hare_2_stack_sortable.py new file mode 100644 index 0000000..fdbef08 --- /dev/null +++ b/playground/hare_2_stack_sortable.py @@ -0,0 +1,55 @@ +from cayley_permutations import CayleyPermutation +from gridded_cayley_permutations import Tiling, GriddedCayleyPerm +from mapplings import MappedTiling, Parameter +from gridded_cayley_permutations.row_col_map import RowColMap +from mapplings.strategies import MappedTileScopePack +from comb_spec_searcher import CombinatorialSpecificationSearcher +import json +from mapplings.cleaners import MTCleaner, ParamCleaner + +# MTCleaner.global_debug_toggle(2) +# ParamCleaner.global_debug_toggle(2) + +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 3, 0]), []) +ghost = til.add_obstructions([GriddedCayleyPerm(CayleyPermutation([0]), [(2, 8)])]) +avoiding_parameters = [ + Parameter(ghost, RowColMap({i: 0 for i in range(9)}, {i: 0 for i in range(9)})) +] +mappling = MappedTiling( + Tiling( + [ + GriddedCayleyPerm( + CayleyPermutation([1, 2, 3, 0]), ((0, 0), (0, 0), (0, 0), (0, 0)) + ), + ], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +pack = MappedTileScopePack.point_placement(mappling) +searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=False) + +spec = searcher.auto_search(status_update=30) +spec.show() + + +json_dict = spec.to_jsonable() +json_str = json.dumps(json_dict) +with open("hare_2_stack_sortable.json", "w") as f: + f.write(json_str) + +new_spec = spec.expand_verified() +new_spec.show() + +json_dict = new_spec.to_jsonable() +json_str = json.dumps(json_dict) +with open("hare_2_stack_sortable_expanded.json", "w") as f: + f.write(json_str) + +new_spec.get_genf() +print([new_spec.count_objects_of_size(i) for i in range(10)]) + +spec.sanity_check() From 9f9a5b178a13b0f97c80375f0893cd1db03da6e9 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:21:29 +0000 Subject: [PATCH 07/24] Fix Remove Blank One down, so many to go. --- mapplings/cleaners/parameter_cleaner.py | 137 +++++++----------------- mapplings/parameter.py | 19 ++++ 2 files changed, 58 insertions(+), 98 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index db15376..1e0cecc 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -1,6 +1,6 @@ """Module with the parameter cleaner""" -from typing import Iterable +from typing import Iterator from itertools import chain from gridded_cayley_permutations.row_col_map import RowColMap from gridded_cayley_permutations.unplacement import PartialUnplacement @@ -60,108 +60,49 @@ 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""" - columns, rows = param.find_blank_columns_and_rows() - cols_to_remove, rows_to_remove = set(), set() - if param.requirements: - req_cols, req_rows = zip( - *chain(*(set(req.positions) for req in chain(*param.requirements))) - ) - else: - req_cols, req_rows = tuple(), tuple() - - def check_for_blank(columns: Iterable[int], image: int, check_rows: bool): - for col in columns: - if check_rows: - if col in rows_to_remove: - break - if param.row_map[col] == image and col not in req_rows: - rows_to_remove.add(col) - else: - break - else: - if col in cols_to_remove: - break - if param.col_map[col] == image and col not in req_cols: - cols_to_remove.add(column) - else: - break - - for column in columns: - image_col = param.map.col_map[column] - cols_to_remove.add(column) - check_for_blank(range(column - 1, -1, -1), image_col, False) - check_for_blank(range(column + 1, param.dimensions[0]), image_col, False) - for blank_row in rows: - image_row = param.row_map[blank_row] - rows_to_remove.add(blank_row) - check_for_blank(range(blank_row - 1, -1, -1), image_row, True) - check_for_blank(range(blank_row + 1, param.dimensions[1]), image_row, True) + blank = tuple(map(set, param.blank_and_near_blank())) + req_cols, req_rows = map( + set[int], + zip(*chain(*(set(req.positions) for req in chain(*param.requirements)))), + ) + col_preimages, row_preimages = param.map.preimage_map() + try: + cols_with_point, _ = map(set[int], zip(*param.single_position_cells())) + except ValueError: + cols_with_point = set[int]() + try: + _, rows_with_point = map(set[int], zip(*param.single_value_cells())) + except ValueError: + rows_with_point = set[int]() + + splits = cols_with_point | req_cols, rows_with_point | req_rows + + def to_remove( + preimages: dict[int, tuple[int, ...]], find_rows: bool + ) -> Iterator[set[int]]: + for preimage in sorted(preimages.values()): + if not set(preimage) & blank[find_rows]: + continue + slice_start = 0 + for i, check in enumerate(preimage): + if check in splits[find_rows]: + section = set(preimage[slice_start:i]) + if len(section) > 1: + try: + yield section - {tuple(section & blank[find_rows])[0]} + except IndexError: + pass + slice_start = i + 1 + if slice_start == 0: + yield set(preimage) + + cols_to_remove = set(chain(*to_remove(col_preimages, False))) + rows_to_remove = set(chain(*to_remove(row_preimages, True))) if ( len(cols_to_remove) == param.dimensions[0] or len(rows_to_remove) == param.dimensions[1] ): return Parameter(Tiling([], [], (1, 1)), RowColMap({0: 0}, {0: 0})) - - single_pos, single_val = ( - param.single_position_cells(), - param.single_value_cells(), - ) - cols_with_point, rows_with_point = set[int](), set[int]() - if single_pos: - cols_with_point, _ = zip(*single_pos) - if single_val: - _, rows_with_point = zip(*single_val) - temp_cols, temp_rows = set(), set() - for col in cols_to_remove: - if ( - col - 1 in cols_with_point - and param.col_map[col] == param.col_map[col - 1] - ): - if ( - col + 1 in cols_to_remove - and param.col_map[col] == param.col_map[col + 1] - ): - temp_cols.add(col + 1) - else: - temp_cols.add(col) - cols_to_remove = set() - for col in temp_cols: - if ( - col + 1 in cols_with_point - and param.col_map[col] == param.col_map[col + 1] - ): - if ( - col - 1 in temp_cols - and param.col_map[col] == param.col_map[col - 1] - ): - cols_to_remove.add(col - 1) - else: - cols_to_remove.add(col) - for row in rows_to_remove: - if ( - row - 1 in rows_with_point - and param.row_map[row] == param.row_map[row - 1] - ): - if ( - row + 1 in rows_to_remove - and param.row_map[row] == param.row_map[row + 1] - ): - temp_rows.add(row + 1) - else: - temp_rows.add(row) - rows_to_remove = set() - for row in temp_rows: - if ( - row + 1 in rows_with_point - and param.row_map[row] == param.row_map[row + 1] - ): - if ( - row - 1 in temp_rows - and param.row_map[row] == param.row_map[row - 1] - ): - rows_to_remove.add(row - 1) - else: - rows_to_remove.add(row) return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 02ab439..7fd9ec8 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -142,6 +142,25 @@ def update_active_cells(self, tiling: Tiling) -> "Parameter": temp.active_cells = self.active_cells return temp + def blank_and_near_blank(self) -> tuple[tuple[int, ...], tuple[int, ...]]: + 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]]: From 3b8df1b367d4bfb4dacaaa6ecba3b5efca154590 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:32:31 +0000 Subject: [PATCH 08/24] Blank Row/Col Fixes Fixes mentioned in discord --- mapplings/cleaners/parameter_cleaner.py | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 1e0cecc..bd11288 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -60,20 +60,29 @@ 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, param.blank_and_near_blank())) - req_cols, req_rows = map( - set[int], - zip(*chain(*(set(req.positions) for req in chain(*param.requirements)))), - ) + blank = tuple(map(set[int], param.blank_and_near_blank())) + if not any(blank): + return param + col_preimages, row_preimages = param.map.preimage_map() + + req_cols, req_rows = set[int](), set[int]() + if param.requirements: + req_cols, req_rows = map( + set[int], + zip( + *chain(*(set(req.positions) for req in chain(*param.requirements))) + ), + ) + cols_with_point, rows_with_point = set[int](), set[int]() try: cols_with_point, _ = map(set[int], zip(*param.single_position_cells())) except ValueError: - cols_with_point = set[int]() + pass try: _, rows_with_point = map(set[int], zip(*param.single_value_cells())) except ValueError: - rows_with_point = set[int]() + pass splits = cols_with_point | req_cols, rows_with_point | req_rows @@ -87,12 +96,11 @@ def to_remove( for i, check in enumerate(preimage): if check in splits[find_rows]: section = set(preimage[slice_start:i]) - if len(section) > 1: - try: - yield section - {tuple(section & blank[find_rows])[0]} - except IndexError: - pass slice_start = i + 1 + try: + yield section - {tuple(section & blank[find_rows])[0]} + except ValueError: + pass if slice_start == 0: yield set(preimage) From ba14bd40922697fee438db8359ca1f009aac0142 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:45:17 +0000 Subject: [PATCH 09/24] Param Comparison --- mapplings/parameter.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 7fd9ec8..07fd7e2 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -2,7 +2,7 @@ from collections import defaultdict from copy import copy -from typing import Iterator, Iterable +from typing import Iterator, Iterable, Optional from itertools import product, chain from cayley_permutations import CayleyPermutation @@ -408,6 +408,42 @@ def to_html_representation(self) -> str: return "".join(result) + def compare_to( + self, other: "Parameter", depth: int = 4 + ) -> tuple[bool, Optional[GriddedCayleyPerm]]: + """Compares the gcps that live on self to the gcps on other up to size depth""" + + col_map = { + val: key + for key, val in enumerate( + sorted(set(self.col_map.values()) | set(other.col_map.values())) + ) + } + row_map = { + val: key + for key, val in enumerate( + sorted(set(self.row_map.values()) | set(other.row_map.values())) + ) + } + self_reduction = RowColMap( + {key: col_map[val] for key, val in self.col_map.items()}, + {key: row_map[val] for key, val in self.row_map.items()}, + ) + other_reduction = RowColMap( + {key: col_map[val] for key, val in other.col_map.items()}, + {key: row_map[val] for key, val in other.row_map.items()}, + ) + temp_self = Parameter(self.ghost, self_reduction) + temp_other = Parameter(other.ghost, other_reduction) + base = Tiling([], [], (len(col_map), len(row_map))) + i = 0 + while i < depth: + for gcp in base.objects_of_size(i): + if temp_self.gcp_has_preimage(gcp) != temp_other.gcp_has_preimage(gcp): + return False, gcp + i += 1 + return True, None + def objects_of_size(self, n, **parameters) -> Iterator[GriddedCayleyPerm]: """Return gridded Cayley permutations of size n in the tiling.""" for val in self.get_objects(n).values(): From e0790254686b2d8a14b3b1594a209308100e8c4f Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:49:08 +0000 Subject: [PATCH 10/24] Different Error --- mapplings/cleaners/parameter_cleaner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index bd11288..2b0483b 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -99,7 +99,7 @@ def to_remove( slice_start = i + 1 try: yield section - {tuple(section & blank[find_rows])[0]} - except ValueError: + except IndexError: pass if slice_start == 0: yield set(preimage) From a370b9f25eb4c16d3ed378536bbfd94ac2816b2a Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:27:53 +0000 Subject: [PATCH 11/24] No ParamCleaner Debug --- tests/cleaner/mappling_cleaner/test_insert_containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cleaner/mappling_cleaner/test_insert_containers.py b/tests/cleaner/mappling_cleaner/test_insert_containers.py index 76c3e6a..8a9c19b 100644 --- a/tests/cleaner/mappling_cleaner/test_insert_containers.py +++ b/tests/cleaner/mappling_cleaner/test_insert_containers.py @@ -11,7 +11,7 @@ from mapplings.cleaners import MTCleaner, ParamCleaner MTCleaner.DEBUG = 2 -ParamCleaner.DEBUG = 2 +ParamCleaner.DEBUG = 0 @pytest.mark.skip(reason="strategy not working yet") From e9d779762981092b9d4f664dc813fe91ee947e78 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:32:19 +0000 Subject: [PATCH 12/24] tox --- mapplings/cleaners/mappling_cleaner.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 186711e..0effd4c 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -469,7 +469,6 @@ def _reduce_parameter_gcps(mappling: MappedTiling, param: Parameter) -> Paramete param.map.preimage_of_requirements(mappling.requirements), mappling.dimensions, ) - point_cells = param.point_cells() mappling_point_cells = mappling.point_cells() simplify.remove_factors_from_obstructions() simplify.remove_redundant_obstructions() From e47e57e2881b420e3d7b939314824b041b175e53 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:38:13 +0000 Subject: [PATCH 13/24] Docstring --- mapplings/parameter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 07fd7e2..74f13fe 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -143,6 +143,7 @@ def update_active_cells(self, tiling: Tiling) -> "Parameter": return temp 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: From 1b62521c3ba9ba290769c1f5e413a3bda3af2b9d Mon Sep 17 00:00:00 2001 From: Abigail Ollson Date: Tue, 3 Feb 2026 10:32:17 +0000 Subject: [PATCH 14/24] table 1 --- playground/table 1.py | 303 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 playground/table 1.py diff --git a/playground/table 1.py b/playground/table 1.py new file mode 100644 index 0000000..2c9ffe4 --- /dev/null +++ b/playground/table 1.py @@ -0,0 +1,303 @@ +from cayley_permutations import CayleyPermutation +from gridded_cayley_permutations import Tiling, GriddedCayleyPerm +from mapplings import MappedTiling, Parameter, ParameterList +from gridded_cayley_permutations.row_col_map import RowColMap +from mapplings.strategies.mapped_tilescope import MappedTileScopePack +from comb_spec_searcher import CombinatorialSpecificationSearcher +import json +from mapplings.cleaners import MTCleaner, ParamCleaner + +MTCleaner.global_debug_toggle(2) + +run_time = 3600 * 5 +debug = True + +from_table = [] +"""Row 1""" +## Basis 1 ## +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost = til.delete_columns([4]) + +til2 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost2 = til2.delete_columns([2]) + +avoiding_parameters = [ + Parameter(ghost, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost2, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) + +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_1.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 1 basis 1: {e}") + +## Basis 2 ## + +til3 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost3 = til3.delete_columns([4]) + +til4 = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost4 = til4.delete_columns([2]) + +avoiding_parameters = [ + Parameter(ghost3, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost4, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) + +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_2.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 1 basis 2: {e}") + +"""Row 2""" +## Basis 1 ## +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost = til.delete_columns([4]) + +til2 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost2 = til2.delete_columns([4]) + +avoiding_parameters = [ + Parameter(ghost, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost2, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_3.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 2 basis 1: {e}") + +## Basis 2 ## + +til3 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost3 = til3.delete_columns([2]) + +til4 = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost4 = til4.delete_columns([2]) + +avoiding_parameters = [ + Parameter(ghost3, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost4, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_4.json", "w") as f: + f.write(json_str) + spec.get_genf() +except Exception as e: + print(f"Failed row 2 basis 2: {e}") +from_table.append(mappling) + +"""Row 3""" +## Basis 1 ## +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost = til.delete_columns([4]) + +til2 = MappedTiling.from_vincular_with_obs(CayleyPermutation([1, 2, 0]), []) +ghost2 = til2.delete_columns([4]) + +avoiding_parameters = [ + Parameter(ghost, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost2, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_5.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 3 basis 1: {e}") + +## Basis 2 ## + +til3 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost3 = til2.delete_columns([4]) + +til4 = MappedTiling.from_vincular_with_obs(CayleyPermutation([1, 0, 2]), []) +ghost4 = til2.delete_columns([4]) + +avoiding_parameters = [ + Parameter(ghost3, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost4, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_6.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 3 basis 2: {e}") + +## Basis 3 ## +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) +ghost = til.delete_columns([2]) + +til2 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 0, 1]), []) +ghost2 = til2.delete_columns([2]) + +avoiding_parameters = [ + Parameter(ghost, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost2, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_7.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 3 basis 3: {e}") + +## Basis 4 ## + +til3 = MappedTiling.from_vincular_with_obs(CayleyPermutation([2, 1, 0]), []) +ghost3 = til3.delete_columns([2]) + +til4 = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 2, 1]), []) +ghost4 = til4.delete_columns([2]) + +avoiding_parameters = [ + Parameter(ghost3, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), + Parameter(ghost4, RowColMap({i: 0 for i in range(6)}, {i: 0 for i in range(7)})), +] +mappling = MappedTiling( + Tiling( + [GriddedCayleyPerm(CayleyPermutation([0, 0]), [(0, 0), (0, 0)])], + [], + (1, 1), + ), + avoiding_parameters, + [], + [], +) +try: + pack = MappedTileScopePack.point_placement(mappling) + searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=debug) + spec = searcher.auto_search(status_update=30, max_expansion_time=run_time) + spec.show() + json_dict = spec.to_jsonable() + json_str = json.dumps(json_dict) + with open("table_1_mappling_8.json", "w") as f: + f.write(json_str) + spec.get_genf() + from_table.append(mappling) +except Exception as e: + print(f"Failed row 3 basis 4: {e}") From 6387e477f9b68d82d9c0a982aa5b3353ed2dca4a Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:08:52 +0000 Subject: [PATCH 15/24] Small Clean Up added requirement_cells function and adjusted remove_blank. --- mapplings/cleaners/parameter_cleaner.py | 25 ++++++------------------- mapplings/parameter.py | 6 ++++++ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 2b0483b..72f1dcb 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -66,25 +66,10 @@ def remove_blank_rows_and_cols(param: Parameter) -> Parameter: col_preimages, row_preimages = param.map.preimage_map() - req_cols, req_rows = set[int](), set[int]() - if param.requirements: - req_cols, req_rows = map( - set[int], - zip( - *chain(*(set(req.positions) for req in chain(*param.requirements))) - ), - ) - cols_with_point, rows_with_point = set[int](), set[int]() try: - cols_with_point, _ = map(set[int], zip(*param.single_position_cells())) + splits = tuple(map(set[int], zip(*param.requirement_cells()))) except ValueError: - pass - try: - _, rows_with_point = map(set[int], zip(*param.single_value_cells())) - except ValueError: - pass - - splits = cols_with_point | req_cols, rows_with_point | req_rows + splits = set[int](), set[int]() def to_remove( preimages: dict[int, tuple[int, ...]], find_rows: bool @@ -92,6 +77,9 @@ def to_remove( for preimage in sorted(preimages.values()): if not set(preimage) & blank[find_rows]: continue + if not splits[find_rows]: + yield set(preimage) + continue slice_start = 0 for i, check in enumerate(preimage): if check in splits[find_rows]: @@ -110,7 +98,7 @@ def to_remove( len(cols_to_remove) == param.dimensions[0] or len(rows_to_remove) == param.dimensions[1] ): - return Parameter(Tiling([], [], (1, 1)), RowColMap({0: 0}, {0: 0})) + return Parameter(Tiling([], [], (0, 0)), RowColMap({}, {})) return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod @@ -163,7 +151,6 @@ def _fuse_valid_rows_or_cols(param: Parameter, fuse_rows: bool) -> Parameter: new_ghost = new_ghost.delete_columns([new_idx]) del new_maps[fuse_rows][old_idx + extend] extend += 1 - continue old_idx += extend new_idx += 1 extend = 1 diff --git a/mapplings/parameter.py b/mapplings/parameter.py index 74f13fe..bc9ed85 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -142,6 +142,12 @@ 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): From 64420916b0acc519ae4b65a0e7e43577d2607ada Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:25:33 +0000 Subject: [PATCH 16/24] Oops typing error --- mapplings/cleaners/parameter_cleaner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 72f1dcb..91f7b4e 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -67,7 +67,8 @@ def remove_blank_rows_and_cols(param: Parameter) -> Parameter: col_preimages, row_preimages = param.map.preimage_map() try: - splits = tuple(map(set[int], zip(*param.requirement_cells()))) + req_cols, req_rows = map(set[int], zip(*param.requirement_cells())) + splits = req_cols, req_rows except ValueError: splits = set[int](), set[int]() From 81014bffdc8a6a94d789287089238a33bd856a1b Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:05:42 +0000 Subject: [PATCH 17/24] Test Fix --- mapplings/cleaners/parameter_cleaner.py | 1 + tests/cleaner/param_cleaner/test_remove_blank_rowcols.py | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 91f7b4e..44a7dd2 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -152,6 +152,7 @@ def _fuse_valid_rows_or_cols(param: Parameter, fuse_rows: bool) -> Parameter: new_ghost = new_ghost.delete_columns([new_idx]) del new_maps[fuse_rows][old_idx + extend] extend += 1 + continue old_idx += extend new_idx += 1 extend = 1 diff --git a/tests/cleaner/param_cleaner/test_remove_blank_rowcols.py b/tests/cleaner/param_cleaner/test_remove_blank_rowcols.py index 7ae9ef2..58d6409 100644 --- a/tests/cleaner/param_cleaner/test_remove_blank_rowcols.py +++ b/tests/cleaner/param_cleaner/test_remove_blank_rowcols.py @@ -32,9 +32,7 @@ def test_remove_blank_rowcols(): ParameterList(frozenset()), ( ParameterList( - frozenset( - {Parameter(Tiling((), (), (1, 1)), RowColMap({0: 0}, {0: 0}))} - ) + frozenset({Parameter(Tiling([], [], (0, 0)), RowColMap({}, {}))}) ), ), (), From efb2f5e65f2f2ed8f98d8dd2c5fa25f949df8ba8 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:15:41 +0000 Subject: [PATCH 18/24] Ghost Fusion --- mapplings/cleaners/parameter_cleaner.py | 79 ++++++++++++++++--------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 44a7dd2..996d544 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -1,6 +1,6 @@ """Module with the parameter cleaner""" -from typing import Iterator +from typing import Iterator, Iterable from itertools import chain from gridded_cayley_permutations.row_col_map import RowColMap from gridded_cayley_permutations.unplacement import PartialUnplacement @@ -26,15 +26,29 @@ class ParamCleaner(GenericCleaner[Parameter]): ) all_loggers = {global_tracker} + # Final Methods @staticmethod @reg(3, run_on_enumerators=False) def reduce_by_fusion(param: Parameter) -> Parameter: """Fuses valid rows and columns""" - return ParamCleaner._fuse_valid_rows_or_cols( - ParamCleaner._fuse_valid_rows_or_cols(param, True), False + deleted_cols, deleted_rows = ParamCleaner._find_indixes_to_fuse( + param, False + ), ParamCleaner._find_indixes_to_fuse(param, True) + temp = Parameter( + Tiling(param.obstructions, [], param.dimensions, False), param.map + ).delete_rows_and_columns(deleted_cols, deleted_rows) + new_ghost = Tiling( + temp.obstructions, + [ + ParamCleaner._make_adjustment_map( + param, deleted_cols, deleted_rows + ).map_gridded_cperms(param.minimal_gridded_cperms()) + ], + temp.dimensions, ) + return Parameter(new_ghost, temp.map) @staticmethod @reg(0) @@ -137,28 +151,37 @@ def unplace_points(param: Parameter) -> Parameter: # Internal Methods @staticmethod - def _fuse_valid_rows_or_cols(param: Parameter, fuse_rows: bool) -> Parameter: - """fully fuses rows or cols of the parameter if they are fusable and map to the same index. - direction = 0 for cols, directions = 1 for rows""" - new_ghost = param.ghost - new_maps = [param.col_map, param.row_map] - old_idx, new_idx, extend = 0, 0, 1 - while old_idx + extend < param.dimensions[fuse_rows]: - if new_maps[fuse_rows][old_idx] == new_maps[fuse_rows][old_idx + extend]: - if new_ghost.is_fusable(fuse_rows, new_idx): - if fuse_rows: - new_ghost = new_ghost.delete_rows([new_idx]) - else: - new_ghost = new_ghost.delete_columns([new_idx]) - del new_maps[fuse_rows][old_idx + extend] - extend += 1 - continue - old_idx += extend - new_idx += 1 - extend = 1 - new_direction_map = { - idx: new_maps[fuse_rows][value] - for idx, value in enumerate(new_maps[fuse_rows].keys()) - } - new_maps[fuse_rows] = new_direction_map - return Parameter(new_ghost, RowColMap(*new_maps)) + def _find_indixes_to_fuse(param: Parameter, fuse_rows: bool) -> Iterator[int]: + """Yields all indices that can be fused""" + maps = param.col_map, param.row_map + temp = Parameter( + Tiling(param.obstructions, [], param.dimensions, False), param.map + ) + for i in range(param.dimensions[fuse_rows] - 1): + if maps[fuse_rows][i] == maps[fuse_rows][i + 1]: + if temp.is_fusable(fuse_rows, i): + yield i + + @staticmethod + def _make_adjustment_map( + original_param: Parameter, + deleted_cols: Iterable[int], + deleted_rows: Iterable[int], + ) -> RowColMap: + """Makes a map from original param to that param after cols and rows are deleted""" + col_correction, row_correction = dict[int, int](), dict[int, int]() + adjust = 0 + for i in range(original_param.dimensions[0]): + if i in deleted_cols: + col_correction[i] = col_correction[i - 1] + adjust += 1 + else: + col_correction[i] = i - adjust + adjust = 0 + for i in range(original_param.dimensions[1]): + if i in deleted_rows: + row_correction[i] = row_correction[i - 1] + adjust += 1 + else: + row_correction[i] = i - adjust + return RowColMap(col_correction, row_correction) From b2789d9be6ca330a5e36666966a1305fab1af3ca Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:23:03 +0000 Subject: [PATCH 19/24] Test Fix --- mapplings/cleaners/mappling_cleaner.py | 12 ++++++------ mapplings/cleaners/parameter_cleaner.py | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 0effd4c..a255303 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -275,16 +275,16 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling: ParameterList( c_list.apply_to_all(MTCleaner._cayley_ob_adjust_param, (ob,)) ) - for c_list in mappling.containing_parameters + for c_list in new_mappling.containing_parameters ) new_enumerators = tuple( ParameterList( e_list.apply_to_all(MTCleaner._cayley_ob_adjust_param, (ob,)) ) - for e_list in mappling.enumerating_parameters + for e_list in new_mappling.enumerating_parameters ) new_mappling = MappedTiling( - mappling.tiling, new_avoiders, new_containers, new_enumerators + new_mappling.tiling, new_avoiders, new_containers, new_enumerators ) else: new_avoiders = tuple( @@ -298,16 +298,16 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling: ParameterList( c_list.apply_to_all(MTCleaner._ob_adjust_param, (ob,)) ) - for c_list in mappling.containing_parameters + for c_list in new_mappling.containing_parameters ) new_enumerators = tuple( ParameterList( e_list.apply_to_all(MTCleaner._ob_adjust_param, (ob,)) ) - for e_list in mappling.enumerating_parameters + for e_list in new_mappling.enumerating_parameters ) new_mappling = MappedTiling( - mappling.tiling, new_avoiders, new_containers, new_enumerators + new_mappling.tiling, new_avoiders, new_containers, new_enumerators ) return new_mappling diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 996d544..fcf912a 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -39,6 +39,8 @@ def reduce_by_fusion(param: Parameter) -> Parameter: temp = Parameter( Tiling(param.obstructions, [], param.dimensions, False), param.map ).delete_rows_and_columns(deleted_cols, deleted_rows) + if not param.requirements: + return temp new_ghost = Tiling( temp.obstructions, [ From 80f620aed2b7f1748f45004a8d817e75cf097fcd Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:38:54 +0000 Subject: [PATCH 20/24] Redundant Parameters A rewrite to simple reduce redundant that makes it less simple --- mapplings/parameter.py | 11 +++++- mapplings/parameter_list.py | 70 +++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/mapplings/parameter.py b/mapplings/parameter.py index bc9ed85..e1c7c50 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -295,7 +295,16 @@ def sub_parameter(self, cells: Iterable[Cell]) -> "Parameter": 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..1de0098 100644 --- a/mapplings/parameter_list.py +++ b/mapplings/parameter_list.py @@ -72,20 +72,68 @@ 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 temp_bigger.requirements: + # any req list that doesnt have any req implied by all reqs of a small req list + if all( + all( + any(small_req.avoids([req]) for small_req in small_list) + for small_list in smaller.requirements + ) + for req in req_list + ): + 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 to_html(self) -> str: From 702a5047c550775d2bb965651d05429beffa0307 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:17:28 +0000 Subject: [PATCH 21/24] Remove Redundant No longer simple --- mapplings/algorithms/param_redundancy.py | 143 ++++++++++++++++++++ mapplings/cleaners/mappling_cleaner.py | 6 +- mapplings/parameter_list.py | 158 +++++++++++++++++++++-- 3 files changed, 294 insertions(+), 13 deletions(-) create mode 100644 mapplings/algorithms/param_redundancy.py 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/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/parameter_list.py b/mapplings/parameter_list.py index 1de0098..f32f812 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 @@ -91,14 +95,9 @@ def compare(smaller: Parameter, bigger: Parameter) -> bool: if not reverse: if any(smaller.gcp_in_tiling(ob) for ob in temp_bigger.obstructions): return False - for req_list in temp_bigger.requirements: - # any req list that doesnt have any req implied by all reqs of a small req list - if all( - all( - any(small_req.avoids([req]) for small_req in small_list) - for small_list in smaller.requirements - ) - for req in req_list + for req_list in smaller.requirements: + if not Simplify.requirement_implied_by_some_requirement( + req_list, temp_bigger.requirements ): return False return True @@ -136,6 +135,10 @@ def compare(smaller: Parameter, bigger: Parameter) -> bool: 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))) @@ -175,3 +178,138 @@ 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: + 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) From 2b04e87619d1deedbd4d4a4d7e175c67accf7db9 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:15:45 +0000 Subject: [PATCH 22/24] Fixed and New Function Fixed redundancy check. Added a rough version of insert blank --- mapplings/cleaners/parameter_cleaner.py | 70 +++++++++++++++++++++++-- mapplings/parameter_list.py | 46 ++++++++++++++-- 2 files changed, 108 insertions(+), 8 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index d92359f..1ab4b35 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 @@ -30,7 +31,7 @@ class ParamCleaner(GenericCleaner[Parameter]): # Final Methods @staticmethod - @reg(3, run_on_enumerators=False) + @reg(4, 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(3, run_on_enumerators=False) def unplace_points(param: Parameter) -> Parameter: """Unplaces all possible points in the parameter""" algo = PartialUnplacement(param.ghost) @@ -147,6 +148,69 @@ 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: + look_at = [ + req_list + for req_list in param.requirements + if len(req_list) == 1 + and req_list[0].pattern + in (CayleyPermutation((0, 1)), CayleyPermutation((1, 0))) + ] + if not look_at: + return param + look_at = [ + req_list + for req_list in look_at + if ( + {req_list[0].positions[0][0], req_list[0].positions[1][0]}.issubset( + param.point_cols + ) + ) + and (req_list[0].positions[0][0] + 1 == req_list[0].positions[1][0]) + ] + if not look_at: + return param + look_at = [ + req_list + for req_list in look_at + if req_list[0].positions[0][1] == req_list[0].positions[1][1] + ] + if not look_at: + return param + blank_cols = param.find_blank_columns_and_rows()[0] + to_insert = set[int]() + for req_list in look_at: + req = req_list[0] + if param.col_map[req.positions[0][0]] != param.col_map[req.positions[1][0]]: + continue + if req.positions[0][0] - 1 or req.positions[1][0] + 1 in blank_cols: + to_insert.add(req.positions[0][0]) + col_adjust = { + i: i + sum((j < i for j in to_insert)) for i in range(param.dimensions[0]) + } + adjust = RowColMap(col_adjust, {i: i for i in range(param.dimensions[1])}) + new_obs = adjust.map_gridded_cperms(param.obstructions) + new_reqs = adjust.map_requirements(param.requirements) + new_col_map = dict[int, int]() + tweak = 0 + for i in range(param.dimensions[0] + len(to_insert)): + if i - tweak - 1 in to_insert: + new_col_map[i] = new_col_map[i - 1] + tweak += 1 + else: + new_col_map[i] = param.col_map[i - tweak] + + return Parameter( + Tiling( + new_obs, + new_reqs, + (param.dimensions[0] + len(to_insert), param.dimensions[1]), + ), + RowColMap(new_col_map, param.row_map), + ) + # Internal Methods @staticmethod diff --git a/mapplings/parameter_list.py b/mapplings/parameter_list.py index f32f812..0ae84c6 100644 --- a/mapplings/parameter_list.py +++ b/mapplings/parameter_list.py @@ -189,6 +189,8 @@ def __init__(self, params: Iterable[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 ) @@ -208,6 +210,9 @@ def all_maps( 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], ...]]: @@ -218,6 +223,16 @@ def make_maps( 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], @@ -230,6 +245,10 @@ def make_maps( 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 @@ -243,6 +262,10 @@ def make_maps( 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( @@ -283,19 +306,32 @@ def redundancy_check(param1: Parameter, param2: Parameter): 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)) + 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 ): - print("NO", temp_param.map) 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(req_list), param2.requirements + temp_param.map.map_gridded_cperms(temp_req_list), + param2.requirements, ): - print("NO", temp_param.map) return False - print("Yes?", temp_param.map) return True for temp_map in maps: From d69631e6c28ee07507a700783c579c4394f1be2d Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:46:36 +0000 Subject: [PATCH 23/24] Insert blank rows rough version of insert blank rows --- mapplings/cleaners/parameter_cleaner.py | 69 +++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 1ab4b35..98e870d 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -31,7 +31,7 @@ class ParamCleaner(GenericCleaner[Parameter]): # Final Methods @staticmethod - @reg(4, 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( @@ -117,7 +117,7 @@ def to_remove( return param.delete_rows_and_columns(cols_to_remove, rows_to_remove) @staticmethod - @reg(3, 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) @@ -150,7 +150,7 @@ def unplace_points(param: Parameter) -> Parameter: @staticmethod @reg(2, run_on_enumerators=False) - def insert_blank(param: Parameter) -> Parameter: + def insert_blank_cols(param: Parameter) -> Parameter: look_at = [ req_list for req_list in param.requirements @@ -210,6 +210,69 @@ def insert_blank(param: Parameter) -> Parameter: ), RowColMap(new_col_map, param.row_map), ) + + @staticmethod + @reg(3, run_on_enumerators=False) + def insert_blank_rows(param: Parameter) -> Parameter: + look_at = [ + req_list + for req_list in param.requirements + if len(req_list) == 1 + and req_list[0].pattern + in (CayleyPermutation((0, 1)), CayleyPermutation((1, 0))) + ] + if not look_at: + return param + look_at = [ + req_list + for req_list in look_at + if ( + {req_list[0].positions[0][1], req_list[0].positions[1][1]}.issubset( + param.point_rows + ) + ) + and (req_list[0].positions[0][1] + 1 == req_list[0].positions[1][1]) + ] + if not look_at: + return param + look_at = [ + req_list + for req_list in look_at + if req_list[0].positions[0][0] == req_list[0].positions[1][0] + ] + if not look_at: + return param + blank_rows = param.find_blank_columns_and_rows()[1] + to_insert = set[int]() + for req_list in look_at: + req = req_list[0] + if param.row_map[req.positions[0][1]] != param.row_map[req.positions[1][1]]: + continue + if req.positions[0][1] - 1 or req.positions[1][1] + 1 in blank_rows: + to_insert.add(req.positions[0][1]) + row_adjust = { + i: i + sum((j < i for j in to_insert)) for i in range(param.dimensions[1]) + } + adjust = RowColMap({i: i for i in range(param.dimensions[1])}, row_adjust) + new_obs = adjust.map_gridded_cperms(param.obstructions) + new_reqs = adjust.map_requirements(param.requirements) + new_row_map = dict[int, int]() + tweak = 0 + for i in range(param.dimensions[1] + len(to_insert)): + if i - tweak - 1 in to_insert: + new_row_map[i] = new_row_map[i - 1] + tweak += 1 + else: + new_row_map[i] = param.col_map[i - tweak] + + return Parameter( + Tiling( + new_obs, + new_reqs, + (param.dimensions[0], param.dimensions[1] + len(to_insert)), + ), + RowColMap(param.col_map, new_row_map), + ) # Internal Methods From fdbab516d6391bed418256d2e39fad954bf117f2 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:35:34 +0000 Subject: [PATCH 24/24] Updated Functions and Tweaks Made the insertion function actually good. Changed how some strategies work. --- mapplings/cleaners/cleaner.py | 4 +- mapplings/cleaners/parameter_cleaner.py | 160 +++++-------------- mapplings/parameter.py | 45 ++++++ mapplings/strategies/tilescope_strategies.py | 8 +- 4 files changed, 91 insertions(+), 126 deletions(-) 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/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 98e870d..6e2fedc 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -23,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} @@ -150,129 +150,49 @@ def unplace_points(param: Parameter) -> Parameter: @staticmethod @reg(2, run_on_enumerators=False) - def insert_blank_cols(param: Parameter) -> Parameter: - look_at = [ - req_list - for req_list in param.requirements - if len(req_list) == 1 - and req_list[0].pattern - in (CayleyPermutation((0, 1)), CayleyPermutation((1, 0))) - ] - if not look_at: - return param - look_at = [ - req_list - for req_list in look_at - if ( - {req_list[0].positions[0][0], req_list[0].positions[1][0]}.issubset( - param.point_cols - ) - ) - and (req_list[0].positions[0][0] + 1 == req_list[0].positions[1][0]) - ] - if not look_at: - return param - look_at = [ - req_list - for req_list in look_at - if req_list[0].positions[0][1] == req_list[0].positions[1][1] - ] - if not look_at: + def insert_blank(param: Parameter) -> Parameter: + """Inserts a blank col/row in between descents/ascents wherever possible""" + if not param.requirements: return param - blank_cols = param.find_blank_columns_and_rows()[0] - to_insert = set[int]() - for req_list in look_at: - req = req_list[0] - if param.col_map[req.positions[0][0]] != param.col_map[req.positions[1][0]]: + 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 - if req.positions[0][0] - 1 or req.positions[1][0] + 1 in blank_cols: - to_insert.add(req.positions[0][0]) - col_adjust = { - i: i + sum((j < i for j in to_insert)) for i in range(param.dimensions[0]) - } - adjust = RowColMap(col_adjust, {i: i for i in range(param.dimensions[1])}) - new_obs = adjust.map_gridded_cperms(param.obstructions) - new_reqs = adjust.map_requirements(param.requirements) - new_col_map = dict[int, int]() - tweak = 0 - for i in range(param.dimensions[0] + len(to_insert)): - if i - tweak - 1 in to_insert: - new_col_map[i] = new_col_map[i - 1] - tweak += 1 - else: - new_col_map[i] = param.col_map[i - tweak] - - return Parameter( - Tiling( - new_obs, - new_reqs, - (param.dimensions[0] + len(to_insert), param.dimensions[1]), - ), - RowColMap(new_col_map, param.row_map), - ) - - @staticmethod - @reg(3, run_on_enumerators=False) - def insert_blank_rows(param: Parameter) -> Parameter: - look_at = [ - req_list - for req_list in param.requirements - if len(req_list) == 1 - and req_list[0].pattern - in (CayleyPermutation((0, 1)), CayleyPermutation((1, 0))) - ] - if not look_at: - return param - look_at = [ - req_list - for req_list in look_at - if ( - {req_list[0].positions[0][1], req_list[0].positions[1][1]}.issubset( - param.point_rows - ) - ) - and (req_list[0].positions[0][1] + 1 == req_list[0].positions[1][1]) - ] - if not look_at: - return param - look_at = [ - req_list - for req_list in look_at - if req_list[0].positions[0][0] == req_list[0].positions[1][0] - ] - if not look_at: - return param - blank_rows = param.find_blank_columns_and_rows()[1] - to_insert = set[int]() - for req_list in look_at: req = req_list[0] - if param.row_map[req.positions[0][1]] != param.row_map[req.positions[1][1]]: + if req.pattern not in ( + CayleyPermutation((0, 1)), + CayleyPermutation((1, 0)), + ): continue - if req.positions[0][1] - 1 or req.positions[1][1] + 1 in blank_rows: - to_insert.add(req.positions[0][1]) - row_adjust = { - i: i + sum((j < i for j in to_insert)) for i in range(param.dimensions[1]) - } - adjust = RowColMap({i: i for i in range(param.dimensions[1])}, row_adjust) - new_obs = adjust.map_gridded_cperms(param.obstructions) - new_reqs = adjust.map_requirements(param.requirements) - new_row_map = dict[int, int]() - tweak = 0 - for i in range(param.dimensions[1] + len(to_insert)): - if i - tweak - 1 in to_insert: - new_row_map[i] = new_row_map[i - 1] - tweak += 1 - else: - new_row_map[i] = param.col_map[i - tweak] - - return Parameter( - Tiling( - new_obs, - new_reqs, - (param.dimensions[0], param.dimensions[1] + len(to_insert)), - ), - RowColMap(param.col_map, new_row_map), - ) + 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 diff --git a/mapplings/parameter.py b/mapplings/parameter.py index e1c7c50..578cd25 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -290,6 +290,51 @@ 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) 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()))