From 82917fb311ddf5ef724f77d4504803b725e88ffb Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 16 Oct 2025 11:48:54 +0000 Subject: [PATCH 01/38] Algos Added algorithms for IL factoring. --- mapplings/algorithms/__init__.py | 4 +- mapplings/algorithms/factor.py | 99 +++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/mapplings/algorithms/__init__.py b/mapplings/algorithms/__init__.py index bdc5848..bf82084 100644 --- a/mapplings/algorithms/__init__.py +++ b/mapplings/algorithms/__init__.py @@ -1,12 +1,14 @@ """A module for working with mapped tilings.""" -from .factor import Factor +from .factor import Factor, ILFactorInverted, ILFactorNormal from .row_col_sep_mt import LTRowColSeparationMT, LTORERowColSeparationMT from .point_placement import MTRequirementPlacement __all__ = [ "Factor", + "ILFactorNormal", + "ILFactorInverted" "LTRowColSeparationMT", "LTORERowColSeparationMT", "MTRequirementPlacement", diff --git a/mapplings/algorithms/factor.py b/mapplings/algorithms/factor.py index 4329400..b979958 100644 --- a/mapplings/algorithms/factor.py +++ b/mapplings/algorithms/factor.py @@ -1,9 +1,11 @@ """Contains the Factor class""" from itertools import chain, combinations +from functools import cached_property +from gridded_cayley_permutations import GriddedCayleyPerm, Tiling, RowColMap from gridded_cayley_permutations.factors import Factors -from mapplings import MappedTiling, ParameterList +from mapplings import MappedTiling, ParameterList, Parameter from mapplings.cleaners import MTCleaner @@ -40,6 +42,7 @@ def combine_cells_from_parameters(self) -> None: for cell1, cell2 in combinations(region, 2): self.combine_cells(cell1, cell2) + @cached_property def find_factors_as_cells(self): """Finds the factors as cells.""" self.combine_cells_from_parameters() @@ -48,7 +51,7 @@ def find_factors_as_cells(self): def find_factors(self) -> tuple[MappedTiling, ...]: """Creates a new mappling for each factor""" all_factors = tuple[MappedTiling, ...]() - for factor in self.find_factors_as_cells(): + for factor in self.find_factors_as_cells: _factor = set(factor) new_avoiders = ParameterList( @@ -74,3 +77,95 @@ def find_factors(self) -> tuple[MappedTiling, ...]: ) all_factors += (MTCleaner.remove_empty_rows_and_cols(new_mappling),) return all_factors + + +class ILFactorNormal(Factor): + """Does IL factoring with 00 obs as normal""" + + @cached_property + def find_factors_as_cells(self): + self.combine_cells_in_obs_and_reqs() + self.combine_cells_from_parameters() + factors = [] + for val in set(self.cells_dict.values()): + factor = [] + for cell in self.cells: + if self.cells_dict[cell] == val: + factor.append(cell) + factors.append(factor) + + return tuple(sorted(tuple(sorted(f)) for f in factors)) + + def make_enumerators(self): + """Creates the enumerators needed for interleaving""" + factor_rows_and_cols = ( + map(tuple, map(set, zip(*factor))) for factor in self.find_factors_as_cells + ) + factor_rows_and_cols = tuple( + map(lambda x: tuple(chain.from_iterable(x)), zip(*factor_rows_and_cols)) + ) + new_enumerators = set() + dimensions = self.tiling.dimensions + for row in range(dimensions[1]): + if factor_rows_and_cols[1].count(row) > 1: + new_enumerators.add( + ParameterList( + ( + Parameter( + Tiling([], [], (dimensions[0], 2)), + RowColMap( + {i: i for i in range(dimensions[0])}, + {0: row, 1: row}, + ), + ), + ) + ) + ) + for col in range(dimensions[0]): + if factor_rows_and_cols[0].count(col) > 1: + new_enumerators.add( + ParameterList( + ( + Parameter( + Tiling([], [], (2, dimensions[1])), + RowColMap( + {0: col, 1: col}, + {i: i for i in range(dimensions[1])}, + ), + ), + ) + ) + ) + return new_enumerators + + def find_factors(self): + avoiders, containers, enumerators = self.mappling.ace_parameters() + self.mappling = MappedTiling( + self.tiling, + avoiders, + containers, + set(enumerators) | self.make_enumerators(), + ) + return super().find_factors() + + +class ILFactorInverted(ILFactorNormal): + """Does IL factoring with the compliment of 00 obs""" + + def combine_cells_in_obs_and_reqs(self): + new_obs = set() + for row in range(self.tiling.dimensions[1]): + for col1, col2 in combinations(range(self.tiling.dimensions[0]), 2): + new_obs.add(GriddedCayleyPerm((0, 0), ((col1, row), (col2, row)))) + new_obs.symmetric_difference_update(set(self.tiling.obstructions)) + for gcp in new_obs: + if not self.point_row_ob(gcp): + for cell, cell2 in combinations((gcp.find_active_cells()), 2): + self.combine_cells(cell, cell2) + for cell, cell2 in chain.from_iterable( + combinations( + chain.from_iterable(req.find_active_cells() for req in req_list), 2 + ) + for req_list in self.tiling.requirements + ): + self.combine_cells(cell, cell2) From 0bd918fa010ad221cfeabce2c7833b89978ab33f Mon Sep 17 00:00:00 2001 From: Abigail Ollson <145692399+Ollson2921@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:34:30 +0000 Subject: [PATCH 02/38] tox --- .gitignore | 3 ++- mapplings/algorithms/__init__.py | 2 +- mapplings/algorithms/factor.py | 2 +- mapplings/parameter.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9ab8a8a..9cdb06e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST -*.DS_Store \ No newline at end of file +*.DS_Store +.tox/ \ No newline at end of file diff --git a/mapplings/algorithms/__init__.py b/mapplings/algorithms/__init__.py index bf82084..b50dedc 100644 --- a/mapplings/algorithms/__init__.py +++ b/mapplings/algorithms/__init__.py @@ -8,7 +8,7 @@ __all__ = [ "Factor", "ILFactorNormal", - "ILFactorInverted" + "ILFactorInverted", "LTRowColSeparationMT", "LTORERowColSeparationMT", "MTRequirementPlacement", diff --git a/mapplings/algorithms/factor.py b/mapplings/algorithms/factor.py index b979958..fa08617 100644 --- a/mapplings/algorithms/factor.py +++ b/mapplings/algorithms/factor.py @@ -46,7 +46,7 @@ def combine_cells_from_parameters(self) -> None: def find_factors_as_cells(self): """Finds the factors as cells.""" self.combine_cells_from_parameters() - return super().find_factors_as_cells() + return super().find_factors_as_cells def find_factors(self) -> tuple[MappedTiling, ...]: """Creates a new mappling for each factor""" diff --git a/mapplings/parameter.py b/mapplings/parameter.py index c1b8612..e7dd4fc 100644 --- a/mapplings/parameter.py +++ b/mapplings/parameter.py @@ -166,7 +166,7 @@ def sub_parameter(self, cells: Iterable[Cell]) -> "Parameter": def factor(self) -> Iterator["Parameter"]: """Factors the ghost and combines factors with overlapping images.""" - factor_cells = Factors(self.ghost).find_factors_as_cells() + factor_cells = Factors(self.ghost).find_factors_as_cells find_images = self.map.image_rows_and_cols factor_image_rows_and_cols = list( (find_images(*zip(*factor)) for factor in factor_cells) From caf3ac538671afe2c705c3582f32d30dfe52c8f7 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:22:16 +0000 Subject: [PATCH 03/38] Log Added logging to cleaner --- mapplings/cleaners/cleaner.py | 158 +++++++++++++++++++++++++++++----- 1 file changed, 136 insertions(+), 22 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 5ff8456..4218008 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -1,6 +1,6 @@ """Module with the generic cleaner and register classes""" -from typing import TypeVar, Callable, Generic, Iterable +from typing import TypeVar, Callable, Generic, Iterable, Any from time import time from comb_spec_searcher.combinatorial_class import CombinatorialClass @@ -87,27 +87,59 @@ def __str__(self): class GenericCleaner(Generic[T]): - """The class used to clean paramaters. + """A class for cleaning combinatorial objects. Core fuctions are decorated with @reg(index) where index is the order of cleaning DEBUG = 0 skips any debugging DEBUG = 1 checks counts and elapsed time after loop cleaning - DEBUG = 2 checks counts and elapsed time after each function""" + DEBUG = 2 checks counts and elapsed time after each function + LOG = 0 Skips logging + LOG = 1 Enables a global log + LOG = 2 Enables a detailed log + """ DEBUG = 0 + LOG = 0 reg = Register[T]() - - def __init__(self, todo_list: Iterable[Callable[[T], T]]): + global_tracker = { + func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} + for func in reg.registered_functions + } + _currently_tracking = "Unspecified" + _unnamed = 0 + detailed_tracker = dict[str, dict[str, Any]]() + + def __init__( + self, todo_list: Iterable[Callable[[T], T]], tracker_id: str = "Unnamed" + ): self.todo_list: tuple[Callable[[T], T], ...] = tuple( sorted(todo_list, key=self.__class__.reg.sorting_key) ) + if tracker_id == "Unnamed": + self.id = f"Unnamed {self.__class__.__name__} {self.__class__._unnamed}" + self.__class__._unnamed += 1 + else: + self.id = tracker_id + + if ( + self.__class__.LOG >= 2 + and self.id not in self.__class__.detailed_tracker.keys() + ): + self.__class__.detailed_tracker[self.id] = { + func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} + for func in self.todo_list + } + super().__init__() def __call__(self, cleaning_object: T) -> T: """Cleans the input cleaning_object according to the cleaner's todo_list""" - return self.__class__.loop_cleanup(cleaning_object, self.todo_list) + self.__class__._currently_tracking = self.id + new_object = self.__class__.loop_cleanup(cleaning_object, self.todo_list) + self.__class__._currently_tracking = "Unspecified" + return new_object def __repr__(self): - return self.__class__.__name__ + f"({self.todo_list})" + return self.__class__.__name__ + f"({self.todo_list},{self.id})" def __str__(self): return ( @@ -144,6 +176,7 @@ def loop_cleanup( + f"\n{cleaning_object}\n {new_cleaning_object}" + f"\n{repr(cleaning_object)}" ) + return new_cleaning_object @classmethod @@ -163,7 +196,7 @@ def unordered_cleanup( for func in cleaning_list: if not bool(new_cleaning_object): return new_cleaning_object - new_cleaning_object = cls.debug(func)(new_cleaning_object) + new_cleaning_object = cls._debug(cls._log(func))(new_cleaning_object) return new_cleaning_object def tracked_cleanup( @@ -178,7 +211,10 @@ def tracked_cleanup( @classmethod def make_full_cleaner(cls): """Returns an instance of a cleaner with all registered cleaning functions""" - return cls(tuple(sorted(cls.reg.registered_functions, key=cls.reg.sorting_key))) + return cls( + tuple(sorted(cls.reg.registered_functions, key=cls.reg.sorting_key)), + "Full Cleaner", + ) @classmethod def full_cleanup(cls, cleaning_object: T) -> T: @@ -186,27 +222,105 @@ def full_cleanup(cls, cleaning_object: T) -> T: return cls.make_full_cleaner()(cleaning_object) @classmethod - def debug(cls, func: Callable[[T], T]): + def toggle_log(cls, level: int) -> None: + """Using this to set LOG and reset debug tracker""" + assert level in (0, 1, 2) + print("toggle log") + match level: + case 0: + print(f"{cls.__name__} Logging Dissabled") + case 1: + print(f"Logging {cls.__name__} Functions Globally") + case 2: + print(f"Logging {cls.__name__} Instances Seperately") + cls.LOG = level + cls.global_tracker = { + func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} + for func in cls.reg.registered_functions + } + cls.detailed_tracker = {"Unspecified": cls.global_tracker} + + @classmethod + def display_log(cls) -> str: + """Returns a string to display cleaner log data""" + if cls.LOG == 0: + return f"{cls.__name__} logging is disabled" + + if cls.LOG == 1: + data = {"Global Cleaner Data": cls.global_tracker} + if cls.LOG == 2: + data = cls.detailed_tracker + else: + return " " + output = "" + for key, value in data.items(): + output += f"{key} :" + output += f"\n Attempts : {value['Attempts']}" + output += f"\n Successes : {value['Successes']}" + output += f"\n Time Spent : {value['Time Spent']}" + return output + + @classmethod + def _log(cls, func: Callable[[T], T]): + """Function used to log a function each time it is run""" + if cls.DEBUG > 0: + return func + + def wrapper(cleaning_object: T) -> T: + start_time = time() + new_object = func(cleaning_object) + elapsed_time = time() - start_time + changed = new_object != cleaning_object + cls._update_log(func, elapsed_time, changed) + return new_object + + return wrapper + + @classmethod + def _update_log(cls, func: Callable[[T], T], time_spent: float, success: bool): + """Function used to change log data""" + match cls.LOG: + case 0: + pass + case 1: + cls.global_tracker[func.__name__]["Time Spent"] += round(time_spent, 5) + cls.global_tracker[func.__name__]["Attempts"] += 1 + cls.global_tracker[func.__name__]["Successes"] += int(success) + case 2: + cls.detailed_tracker[cls._currently_tracking][func.__name__][ + "Time Spent" + ] += round(time_spent, 5) + cls.detailed_tracker[cls._currently_tracking]["Attempts"] += 1 + cls.detailed_tracker[cls._currently_tracking]["Successes"] += int( + success + ) + + @classmethod + def _debug(cls, func: Callable[[T], T]): """Sets the debug behavior for cleaning functions.""" - if cls.DEBUG > 1: + if cls.DEBUG > 0: def wrapper(cleaning_object: T) -> T: start_time = time() new_object = func(cleaning_object) elapsed_time = time() - start_time changed = new_object != cleaning_object + cls._update_log(func, elapsed_time, changed) if changed: - print( - f"++ {cls.__name__}.{func.__name__} elapsed time : {elapsed_time} ++" - ) - old_counts = cleaning_object.initial_conditions(2) - new_counts = new_object.initial_conditions(2) - assert old_counts == new_counts, ( - f"Counts differ:\nInitial counts: {old_counts}\nClean counts: {new_counts}" - + f"\n {cleaning_object}\n {new_object}" - + f"\n {repr(cleaning_object)}" - ) - else: + if cls.DEBUG > 1: + + print( + f"++ {cls.__name__}.{func.__name__} elapsed time : {elapsed_time} ++" + ) + old_counts = cleaning_object.initial_conditions(2) + new_counts = new_object.initial_conditions(2) + assert old_counts == new_counts, ( + f"Counts differ after {cls.__name__}.{func.__name__}:" + + f"\nInitial counts: {old_counts}\nClean counts: {new_counts}" + + f"\n {cleaning_object}\n {new_object}" + + f"\n {repr(cleaning_object)}" + ) + elif cls.DEBUG > 1: print( f"-- {cls.__name__}.{func.__name__} elapsed time : {elapsed_time} --" ) From 4008cbd60257310a080ce1c59c085ab934f1e223 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 27 Nov 2025 08:33:25 +0000 Subject: [PATCH 04/38] OOps Probably want to keep the IL factoring code on the IL factoring branch --- mapplings/algorithms/factor.py | 96 +++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/mapplings/algorithms/factor.py b/mapplings/algorithms/factor.py index 7330e95..4ff09b3 100644 --- a/mapplings/algorithms/factor.py +++ b/mapplings/algorithms/factor.py @@ -3,8 +3,10 @@ from itertools import chain, combinations from functools import cached_property from gridded_cayley_permutations.factors import Factors +from gridded_cayley_permutations import Tiling, GriddedCayleyPerm +from gridded_cayley_permutations.row_col_map import RowColMap -from mapplings import MappedTiling, ParameterList +from mapplings import MappedTiling, ParameterList, Parameter from mapplings.cleaners import MTCleaner @@ -76,3 +78,95 @@ def find_factors(self) -> tuple[MappedTiling, ...]: ) all_factors += (MTCleaner.remove_empty_rows_and_cols(new_mappling),) return all_factors + + +class ILFactorNormal(Factor): + """Does IL factoring with 00 obs as normal""" + + @cached_property + def find_factors_as_cells(self): + self.combine_cells_in_obs_and_reqs() + self.combine_cells_from_parameters() + factors = [] + for val in set(self.cells_dict.values()): + factor = [] + for cell in self.cells: + if self.cells_dict[cell] == val: + factor.append(cell) + factors.append(factor) + + return tuple(sorted(tuple(sorted(f)) for f in factors)) + + def make_enumerators(self): + """Creates the enumerators needed for interleaving""" + factor_rows_and_cols = ( + map(tuple, map(set, zip(*factor))) for factor in self.find_factors_as_cells + ) + factor_rows_and_cols = tuple( + map(lambda x: tuple(chain.from_iterable(x)), zip(*factor_rows_and_cols)) + ) + new_enumerators = set() + dimensions = self.tiling.dimensions + for row in range(dimensions[1]): + if factor_rows_and_cols[1].count(row) > 1: + new_enumerators.add( + ParameterList( + ( + Parameter( + Tiling([], [], (dimensions[0], 2)), + RowColMap( + {i: i for i in range(dimensions[0])}, + {0: row, 1: row}, + ), + ), + ) + ) + ) + for col in range(dimensions[0]): + if factor_rows_and_cols[0].count(col) > 1: + new_enumerators.add( + ParameterList( + ( + Parameter( + Tiling([], [], (2, dimensions[1])), + RowColMap( + {0: col, 1: col}, + {i: i for i in range(dimensions[1])}, + ), + ), + ) + ) + ) + return new_enumerators + + def find_factors(self): + avoiders, containers, enumerators = self.mappling.ace_parameters() + self.mappling = MappedTiling( + self.tiling, + avoiders, + containers, + set(enumerators) | self.make_enumerators(), + ) + return super().find_factors() + + +class ILFactorInverted(ILFactorNormal): + """Does IL factoring with the compliment of 00 obs""" + + def combine_cells_in_obs_and_reqs(self): + new_obs = set() + for row in range(self.tiling.dimensions[1]): + for col1, col2 in combinations(range(self.tiling.dimensions[0]), 2): + new_obs.add(GriddedCayleyPerm((0, 0), ((col1, row), (col2, row)))) + new_obs.symmetric_difference_update(set(self.tiling.obstructions)) + for gcp in new_obs: + if not self.point_row_ob(gcp): + for cell, cell2 in combinations((gcp.find_active_cells()), 2): + self.combine_cells(cell, cell2) + for cell, cell2 in chain.from_iterable( + combinations( + chain.from_iterable(req.find_active_cells() for req in req_list), 2 + ) + for req_list in self.tiling.requirements + ): + self.combine_cells(cell, cell2) From a60ad7a99517b22fde0664694b4638e6ec91e468 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Sat, 29 Nov 2025 15:43:17 +0000 Subject: [PATCH 05/38] Built Log Display Additionally, the index attribute added to functions no longer has a custom name --- mapplings/cleaners/cleaner.py | 169 ++++++++++++------- mapplings/cleaners/mappling_cleaner.py | 2 +- mapplings/cleaners/parameter_cleaner.py | 1 - mapplings/strategies/tilescope_strategies.py | 24 ++- 4 files changed, 132 insertions(+), 64 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 4218008..992973d 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -1,7 +1,9 @@ """Module with the generic cleaner and register classes""" -from typing import TypeVar, Callable, Generic, Iterable, Any +from typing import TypeVar, Callable, Generic, Iterable from time import time +from datetime import timedelta +from tabulate import tabulate from comb_spec_searcher.combinatorial_class import CombinatorialClass @@ -15,23 +17,35 @@ class Register(Generic[T]): Initialize with flag_name = bool to make flag_name an attribute of all registered functions. """ - def __init__(self, attr_name: str = "index", **additional_flags: bool): + def __init__(self, **additional_flags: bool): self.registered_functions: set[Callable[[T], T]] = set() self.map: dict[int, Callable[[T], T]] = {} self.flags = dict(additional_flags) - self.attr_name = attr_name def __call__( - self, idx: int, update_register: bool = True, **flag_updates: bool + self, + idx: int, + update_register: bool = True, + log_id: str | None = None, + **flag_updates: bool, ) -> Callable[[Callable[[T], T]], Callable[[T], T]]: """Used as the decorator to register functions. Setting update_register to False doesn't add the function to registered functions Flag updates will overwrite the register's default flag states.""" def register_function(func: Callable[[T], T]) -> Callable[[T], T]: + assert not hasattr( + func, "index" + ), f"{func.__name__} already has index attribute." + if log_id is None: + setattr(func, "log_id", func.__name__.replace("_", " ").title()) + else: + setattr(func, "log_id", log_id) if update_register: self.add_to_register(func, idx) - setattr(func, self.attr_name, idx) + setattr(func, "index", idx) + self.map[idx] = func + return self.set_flags(func, flag_updates) return register_function @@ -39,11 +53,10 @@ def register_function(func: Callable[[T], T]) -> Callable[[T], T]: def add_to_register(self, func: Callable[[T], T], idx: int) -> None: """Used to add functions to the register""" assert idx not in self.map, ( - f"{self.attr_name} {idx} is already assigned to {self[idx].__qualname__} " + f"Index {idx} is already assigned to {self[idx].__qualname__} " + f"and cannot be assigned to {func.__qualname__}" ) self.registered_functions.add(func) - self.map[idx] = func def set_flags(self, func: Callable[[T], T], flag_updates: dict[str, bool]): """Adds all registered flags as attributes to func. @@ -61,16 +74,14 @@ def set_flags(self, func: Callable[[T], T], flag_updates: dict[str, bool]): def sorting_key(self, func: Callable[[T], T]) -> int: """Used to sort fuctions in cleaners""" - assert hasattr( - func, self.attr_name - ), f"{func.__qualname__} has no assigned {self.attr_name}" - return getattr(func, self.attr_name) + assert hasattr(func, "index"), f"{func.__qualname__} has no assigned index" + return getattr(func, "index") def __getitem__(self, key: int) -> Callable[[T], T]: return self.map[key] def __repr__(self): - output = f"{self.__class__.__name__}(attr_name={self.attr_name}" + output = f"{self.__class__.__name__}(" for key, value in self.flags.items(): output += f", {key}={value}" return output + ")" @@ -79,7 +90,7 @@ def __str__(self): funcs = sorted(self.registered_functions, key=self.sorting_key) output = f"{funcs[0].__qualname__.split('.')[0]} has registered the following functions:" for func in funcs: - output += f"\n{getattr(func, self.attr_name)} : {func.__name__}" + output += f"\n{getattr(func, "index")} : {func.__name__}" for key, value in tuple(func.__dict__.items())[1:]: output += f"\n -{key}={value}" output += "\n" @@ -101,13 +112,14 @@ class GenericCleaner(Generic[T]): DEBUG = 0 LOG = 0 reg = Register[T]() - global_tracker = { - func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} - for func in reg.registered_functions - } _currently_tracking = "Unspecified" _unnamed = 0 - detailed_tracker = dict[str, dict[str, Any]]() + log_tracker = { + "Global Tracker": { + getattr(func, "log_id"): {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} + for func in reg.registered_functions + } + } def __init__( self, todo_list: Iterable[Callable[[T], T]], tracker_id: str = "Unnamed" @@ -121,12 +133,13 @@ def __init__( else: self.id = tracker_id - if ( - self.__class__.LOG >= 2 - and self.id not in self.__class__.detailed_tracker.keys() - ): - self.__class__.detailed_tracker[self.id] = { - func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} + if self.__class__.LOG >= 2 and self.id not in self.__class__.log_tracker.keys(): + self.__class__.log_tracker[self.id] = { + getattr(func, "log_id"): { + "Attempts": 0, + "Successes": 0, + "Time Spent": 0.0, + } for func in self.todo_list } super().__init__() @@ -142,10 +155,11 @@ def __repr__(self): return self.__class__.__name__ + f"({self.todo_list},{self.id})" def __str__(self): - return ( - self.__class__.__name__ - + f"({dict((self.reg.sorting_key(func) , func.__name__) for func in self.todo_list)})" + functions = dict( + (self.reg.sorting_key(func), getattr(func, "log_id")) + for func in self.todo_list ) + return self.__class__.__name__ + f"({functions}, {self.id})" def __iter__(self): return iter(sorted(self.todo_list, key=self.reg.sorting_key)) @@ -234,31 +248,63 @@ def toggle_log(cls, level: int) -> None: case 2: print(f"Logging {cls.__name__} Instances Seperately") cls.LOG = level - cls.global_tracker = { - func.__name__: {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} - for func in cls.reg.registered_functions + cls.log_tracker = { + "Global Tracker": { + getattr(func, "log_id"): { + "Attempts": 0, + "Successes": 0, + "Time Spent": 0.0, + } + for func in cls.reg.registered_functions + } } - cls.detailed_tracker = {"Unspecified": cls.global_tracker} @classmethod def display_log(cls) -> str: """Returns a string to display cleaner log data""" - if cls.LOG == 0: - return f"{cls.__name__} logging is disabled" - + print(cls.LOG) if cls.LOG == 1: - data = {"Global Cleaner Data": cls.global_tracker} - if cls.LOG == 2: - data = cls.detailed_tracker + data = { + f"{cls.__name__} Global Data": cls.log_tracker["Global Tracker"] + }.items() + elif cls.LOG == 2: + data = cls.log_tracker.items() else: - return " " - output = "" - for key, value in data.items(): - output += f"{key} :" - output += f"\n Attempts : {value['Attempts']}" - output += f"\n Successes : {value['Successes']}" - output += f"\n Time Spent : {value['Time Spent']}" - return output + return f"{cls.__name__} logging is disabled" + all_tables = [] + headers = [ + "Attempts", + "Successes", + "Success\n Rate", + "Time Spent", + "Percent\n of Time", + ] + coalign = ("left", "right", "right", "right", "right", "right") + rows = dict[str, float]() + for key, value in data: + temp_headers = [key] + headers + total_time = sum(record["Time Spent"] for record in value.values()) + table = list[tuple[str, int, int, str, timedelta, str]]() + for name, record in value.items(): + ftime = record["Time Spent"] + rows.update(((name, ftime),)) + attempt = int(record["Attempts"]) + success = int(record["Successes"]) + table.append( + ( + name, + attempt, + success, + f"{int((success / attempt) * 100)}%", + timedelta(seconds=int(ftime)), + f"{int((ftime / total_time) * 100)}%", + ) + ) + table.sort(key=lambda row: rows[row[0]], reverse=True) + all_tables.append( + tabulate(table, headers=temp_headers, colalign=coalign) + "\n" + ) + return " " + "\n".join(all_tables) + "\n" @classmethod def _log(cls, func: Callable[[T], T]): @@ -279,21 +325,24 @@ def wrapper(cleaning_object: T) -> T: @classmethod def _update_log(cls, func: Callable[[T], T], time_spent: float, success: bool): """Function used to change log data""" - match cls.LOG: - case 0: - pass - case 1: - cls.global_tracker[func.__name__]["Time Spent"] += round(time_spent, 5) - cls.global_tracker[func.__name__]["Attempts"] += 1 - cls.global_tracker[func.__name__]["Successes"] += int(success) - case 2: - cls.detailed_tracker[cls._currently_tracking][func.__name__][ - "Time Spent" - ] += round(time_spent, 5) - cls.detailed_tracker[cls._currently_tracking]["Attempts"] += 1 - cls.detailed_tracker[cls._currently_tracking]["Successes"] += int( - success - ) + log_id = getattr(func, "log_id") + level = cls.LOG + if level == 0: + return + if level > 0: + cls.log_tracker["Global Tracker"][log_id]["Time Spent"] += round( + time_spent, 4 + ) + cls.log_tracker["Global Tracker"][log_id]["Attempts"] += 1 + cls.log_tracker["Global Tracker"][log_id]["Successes"] += int(success) + if level > 1: + cls.log_tracker[cls._currently_tracking][log_id]["Time Spent"] += round( + time_spent, 4 + ) + cls.log_tracker[cls._currently_tracking][log_id]["Attempts"] += 1 + cls.log_tracker[cls._currently_tracking][log_id]["Successes"] += int( + success + ) @classmethod def _debug(cls, func: Callable[[T], T]): diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index e3cf86d..65e7db5 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -22,7 +22,7 @@ class MTCleaner(GenericCleaner[MappedTiling]): where index determines cleaning order""" DEBUG = 0 - reg = Register[MappedTiling]("mappling_register") + reg = Register[MappedTiling]() # Final Methods @staticmethod diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index ca3ab19..6e11f10 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -16,7 +16,6 @@ class ParamCleaner(GenericCleaner[Parameter]): DEBUG = 0 reg = Register[Parameter]( - "param_register", run_on_avoiders=True, run_on_containers=True, run_on_enumerators=True, diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index dcede9b..f9050b3 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -23,7 +23,12 @@ DIR_LEFT, DIR_RIGHT, ) -from comb_spec_searcher import StrategyPack, DisjointUnionStrategy, AtomStrategy +from comb_spec_searcher import ( + StrategyPack, + DisjointUnionStrategy, + AtomStrategy, + CombinatorialSpecificationSearcher, +) from comb_spec_searcher.exception import StrategyDoesNotApply from cayley_permutations import CayleyPermutation from mapplings import MappedTiling @@ -33,10 +38,25 @@ LTORERowColSeparationMT, LTRowColSeparationMT, ) -from mapplings.cleaners import MTCleaner +from mapplings.cleaners import MTCleaner, ParamCleaner from .verification_strategy import NoParameterVerificationStrategy +MTCleaner.toggle_log(1) +temp = CombinatorialSpecificationSearcher.status + + +def new_status(self, elaborate: bool) -> str: + """Overwrites CSS status method""" + output = ( + temp(self, elaborate) + MTCleaner.display_log() + ParamCleaner.display_log() + ) + return output + + +CombinatorialSpecificationSearcher.status = new_status + + class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): """ A strategy for placing requirements in a mapped tiling. From ba405609eeba950130784314ac8e654db29bbd75 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:25:06 +0000 Subject: [PATCH 06/38] Changes Made the cleaner log a separate class. Now, each cleaner has its own log which can be customized. --- mapplings/cleaners/__init__.py | 3 +- mapplings/cleaners/cleaner.py | 417 +++++++++++-------- mapplings/cleaners/mappling_cleaner.py | 10 +- mapplings/cleaners/parameter_cleaner.py | 6 +- mapplings/strategies/tilescope_strategies.py | 35 +- playground/cleaner_motzkin.py | 6 +- 6 files changed, 298 insertions(+), 179 deletions(-) diff --git a/mapplings/cleaners/__init__.py b/mapplings/cleaners/__init__.py index ba6b6d8..2087859 100644 --- a/mapplings/cleaners/__init__.py +++ b/mapplings/cleaners/__init__.py @@ -1,12 +1,13 @@ """A module for with the cleaning classes.""" -from .cleaner import GenericCleaner, Register +from .cleaner import GenericCleaner, Register, CleanerLog from .parameter_cleaner import ParamCleaner from .mappling_cleaner import MTCleaner __all__ = [ "GenericCleaner", "Register", + "CleanerLog", "ParamCleaner", "MTCleaner", ] diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 992973d..32f65d0 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -3,7 +3,7 @@ from typing import TypeVar, Callable, Generic, Iterable from time import time from datetime import timedelta -from tabulate import tabulate +from tabulate import tabulate, SEPARATING_LINE from comb_spec_searcher.combinatorial_class import CombinatorialClass @@ -97,58 +97,255 @@ def __str__(self): return output +class CleanerLog(Generic[T]): + """A class for tracking cleaners""" + + def __init__( + self, + logged_functions: Iterable[Callable[[T], T]], + log_level: int = 0, + debug_level: int = 0, + name: str = "Unspecified", + ): + self.name = name + self.log_level = log_level + self.debug_level = debug_level + self.functions = logged_functions + print(logged_functions) + self.tracker = { + getattr(func, "log_id"): { + "Attempts": 0, + "Successes": 0, + "Success Time": 0.0, + "Fail Time": 0.0, + } + for func in self.functions + } + self.global_tracker: "CleanerLog" | None = None + self.total_times = [0.0, 0.0] + + def __call__(self, func: Callable[[T], T]): + return self._debug(self._log(func)) + + def reset_log(self) -> dict[str, dict[str, int | float]]: + """Using this to set LOG and reset debug tracker""" + return { + getattr(func, "log_id"): { + "Attempts": 0, + "Successes": 0, + "Success Time": 0.0, + "Fail Time": 0.0, + } + for func in self.functions + } + + def wrap_functions( + self, functions: Iterable[Callable[[T], T]] + ) -> Iterable[Callable[[T], T]]: + """Applies the debug and log wrappers to each function""" + return (self._debug(self._log(func)) for func in functions) + + def display(self) -> str: + """Returns a string to display cleaner log data""" + if self.log_level == 0: + return f"\n{self.name} logging is disabled \n" + headers = [ + "", + "\nAttempts", + "\nSuccesses", + "Success\n Rate", + "Time Spent\n on Successes", + "Time Spent\n on Failures", + "Total\nElapsed Time", + "Percent of\n Total Time", + ] + coalign = ( + "left", + "right", + "right", + "right", + "right", + "right", + "right", + "right", + ) + table = list[tuple[str, int, int, str, timedelta, timedelta, timedelta, str]]() + rows = dict[str, float]() + total_attempt = 0 + total_success = 0 + total_time = sum(self.total_times) + if self.global_tracker is None: + time_ratio = 100.0 + else: + global_time = sum(self.global_tracker.total_times) + if global_time == 0: + time_ratio = 100.0 + else: + time_ratio = total_time / global_time * 100 + for name, record in self.tracker.items(): + ftime = record["Success Time"] + record["Fail Time"] + rows.update(((f" {name}", ftime),)) + attempt = int(record["Attempts"]) + success = int(record["Successes"]) + total_attempt += attempt + total_success += success + if self.log_level > 1: + if total_time == 0: + ftime_ratio = 0 + else: + ftime_ratio = int((ftime / total_time) * 100) + table.append( + ( + f" {name}", + attempt, + success, + f"{int((success / (max(attempt, attempt == 0))) * 100)}%", + timedelta(seconds=int(record["Success Time"])), + timedelta(seconds=int(record["Fail Time"])), + timedelta(seconds=int(ftime)), + f"{ftime_ratio}%", + ) + ) + table.sort(key=lambda row: rows[row[0]], reverse=True) + if total_attempt == 0: + attempt_ratio = 0.0 + else: + attempt_ratio = (total_success / total_attempt) * 100 + table = [ + ( + self.name, + total_attempt, + total_success, + f"{int(attempt_ratio)}%", + timedelta(seconds=int(self.total_times[1])), + timedelta(seconds=int(self.total_times[0])), + timedelta(seconds=int(total_time)), + f"{int(time_ratio)}%", + ), + SEPARATING_LINE, + ] + table + + return "\n" + tabulate(table, headers=headers, colalign=coalign) + + def _log(self, func: Callable[[T], T]): + """Function used to log a function each time it is run""" + if self.debug_level > 0 or self.log_level == 0: + return func + + def wrapper(cleaning_object: T) -> T: + start_time = time() + new_object = func(cleaning_object) + elapsed_time = time() - start_time + changed = new_object != cleaning_object + self._update_log(func, elapsed_time, changed) + return new_object + + return wrapper + + def _update_log(self, func: Callable[[T], T], time_spent: float, success: bool): + """Function used to change log data""" + log_id = getattr(func, "log_id") + + if success: + key = "Success Time" + else: + key = "Fail Time" + if self.global_tracker is not None: + if self.global_tracker.log_level > 0: + self.global_tracker.tracker[log_id][key] += round(time_spent, 4) + self.global_tracker.tracker[log_id]["Attempts"] += 1 + self.global_tracker.tracker[log_id]["Successes"] += int(success) + self.global_tracker.total_times[success] += time_spent + if self.log_level > 0: + self.tracker[log_id][key] += round(time_spent, 4) + self.tracker[log_id]["Attempts"] += 1 + self.tracker[log_id]["Successes"] += int(success) + self.total_times[success] += time_spent + + def _debug(self, func: Callable[[T], T]): + """Sets the debug behavior for cleaning functions.""" + if self.debug_level > 0: + + def wrapper(cleaning_object: T) -> T: + start_time = time() + new_object = func(cleaning_object) + elapsed_time = time() - start_time + changed = new_object != cleaning_object + self._update_log(func, elapsed_time, changed) + if changed: + if self.debug_level > 1: + old_counts = cleaning_object.initial_conditions(2) + new_counts = new_object.initial_conditions(2) + assert old_counts == new_counts, ( + f"Counts differ after {self.name}.{func.__name__}:" + + f"\nInitial counts: {old_counts}\nClean counts: {new_counts}" + + f"\n {cleaning_object}\n {new_object}" + + f"\n {repr(cleaning_object)}" + ) + print( + f"++ {self.name}.{func.__name__} elapsed time : {elapsed_time} ++" + ) + elif self.debug_level > 1: + print( + f"-- {self.name}.{func.__name__} elapsed time : {elapsed_time} --" + ) + return new_object + + return wrapper + return func + + class GenericCleaner(Generic[T]): """A class for cleaning combinatorial objects. Core fuctions are decorated with @reg(index) where index is the order of cleaning + DEBUG = 0 skips any debugging DEBUG = 1 checks counts and elapsed time after loop cleaning DEBUG = 2 checks counts and elapsed time after each function + LOG = 0 Skips logging LOG = 1 Enables a global log LOG = 2 Enables a detailed log + + DEBUG and LOG can be changed globally with global toggle functions + or per instance with cleaner_instance.LOG = # or cleaner_instance.DEBUG = # """ DEBUG = 0 LOG = 0 reg = Register[T]() - _currently_tracking = "Unspecified" _unnamed = 0 - log_tracker = { - "Global Tracker": { - getattr(func, "log_id"): {"Attempts": 0, "Successes": 0, "Time Spent": 0.0} - for func in reg.registered_functions - } - } + global_tracker = CleanerLog[T]( + set(reg.registered_functions), LOG, DEBUG, "Global Tracker" + ) + all_loggers = set[CleanerLog]() + _currently_tracking = global_tracker def __init__( self, todo_list: Iterable[Callable[[T], T]], tracker_id: str = "Unnamed" ): - self.todo_list: tuple[Callable[[T], T], ...] = tuple( - sorted(todo_list, key=self.__class__.reg.sorting_key) - ) + if tracker_id == "Unnamed": self.id = f"Unnamed {self.__class__.__name__} {self.__class__._unnamed}" self.__class__._unnamed += 1 else: self.id = tracker_id - - if self.__class__.LOG >= 2 and self.id not in self.__class__.log_tracker.keys(): - self.__class__.log_tracker[self.id] = { - getattr(func, "log_id"): { - "Attempts": 0, - "Successes": 0, - "Time Spent": 0.0, - } - for func in self.todo_list - } + self.logger = CleanerLog[T]( + todo_list, self.__class__.LOG, self.__class__.DEBUG, self.id + ) + self.logger.global_tracker = self.__class__.global_tracker + self.todo_list: tuple[Callable[[T], T], ...] = tuple( + sorted(todo_list, key=self.__class__.reg.sorting_key) + ) + self.__class__.all_loggers.add(self.logger) super().__init__() def __call__(self, cleaning_object: T) -> T: """Cleans the input cleaning_object according to the cleaner's todo_list""" - self.__class__._currently_tracking = self.id + self.__class__._currently_tracking = self.logger new_object = self.__class__.loop_cleanup(cleaning_object, self.todo_list) - self.__class__._currently_tracking = "Unspecified" + self.__class__._currently_tracking = self.__class__.global_tracker return new_object def __repr__(self): @@ -164,6 +361,14 @@ def __str__(self): def __iter__(self): return iter(sorted(self.todo_list, key=self.reg.sorting_key)) + def __setattr__(self, name, value): + if name == "LOG": + self.logger.log_level = value + elif name == "DEBUG": + self.logger.debug_level = value + else: + super().__setattr__(name, value) + @classmethod def loop_cleanup( cls, cleaning_object: T, cleaning_list: Iterable[Callable[[T], T]] @@ -207,15 +412,16 @@ def unordered_cleanup( ) -> T: """Applies all functions in cleaning_list without reordering""" new_cleaning_object = cleaning_object + log = cls._currently_tracking for func in cleaning_list: - if not bool(new_cleaning_object): + if not bool(new_cleaning_object): # fix this return new_cleaning_object - new_cleaning_object = cls._debug(cls._log(func))(new_cleaning_object) + new_cleaning_object = log(func)(new_cleaning_object) return new_cleaning_object def tracked_cleanup( self, cleaning_object: T, cleaning_list: Iterable[Callable[[T], T]] - ) -> T: + ) -> T: # This function is currn """Cleans cleaning_object according to the cleaning list, removes any completed cleaning functions from the cleaner's todo_list""" new_cleaning_object = self.list_cleanup(cleaning_object, cleaning_list) @@ -223,11 +429,11 @@ def tracked_cleanup( return new_cleaning_object @classmethod - def make_full_cleaner(cls): + def make_full_cleaner(cls, name: str = "Full Cleaner"): """Returns an instance of a cleaner with all registered cleaning functions""" return cls( tuple(sorted(cls.reg.registered_functions, key=cls.reg.sorting_key)), - "Full Cleaner", + name, ) @classmethod @@ -236,144 +442,27 @@ def full_cleanup(cls, cleaning_object: T) -> T: return cls.make_full_cleaner()(cleaning_object) @classmethod - def toggle_log(cls, level: int) -> None: - """Using this to set LOG and reset debug tracker""" - assert level in (0, 1, 2) - print("toggle log") - match level: - case 0: - print(f"{cls.__name__} Logging Dissabled") - case 1: - print(f"Logging {cls.__name__} Functions Globally") - case 2: - print(f"Logging {cls.__name__} Instances Seperately") + def global_log_toggle(cls, level: int) -> None: + """Updates the log level of all cleaner instances and resets tracking""" cls.LOG = level - cls.log_tracker = { - "Global Tracker": { - getattr(func, "log_id"): { - "Attempts": 0, - "Successes": 0, - "Time Spent": 0.0, - } - for func in cls.reg.registered_functions - } - } + print("RESET") + for logger in cls.all_loggers: + logger.log_level = level + logger.tracker = logger.reset_log() @classmethod - def display_log(cls) -> str: - """Returns a string to display cleaner log data""" - print(cls.LOG) - if cls.LOG == 1: - data = { - f"{cls.__name__} Global Data": cls.log_tracker["Global Tracker"] - }.items() - elif cls.LOG == 2: - data = cls.log_tracker.items() - else: - return f"{cls.__name__} logging is disabled" - all_tables = [] - headers = [ - "Attempts", - "Successes", - "Success\n Rate", - "Time Spent", - "Percent\n of Time", - ] - coalign = ("left", "right", "right", "right", "right", "right") - rows = dict[str, float]() - for key, value in data: - temp_headers = [key] + headers - total_time = sum(record["Time Spent"] for record in value.values()) - table = list[tuple[str, int, int, str, timedelta, str]]() - for name, record in value.items(): - ftime = record["Time Spent"] - rows.update(((name, ftime),)) - attempt = int(record["Attempts"]) - success = int(record["Successes"]) - table.append( - ( - name, - attempt, - success, - f"{int((success / attempt) * 100)}%", - timedelta(seconds=int(ftime)), - f"{int((ftime / total_time) * 100)}%", - ) - ) - table.sort(key=lambda row: rows[row[0]], reverse=True) - all_tables.append( - tabulate(table, headers=temp_headers, colalign=coalign) + "\n" - ) - return " " + "\n".join(all_tables) + "\n" + def global_debug_toggle(cls, level: int) -> None: + """Applies a debug level to all cleaner instances""" + cls.DEBUG = level + for logger in cls.all_loggers: + logger.debug_level = level @classmethod - def _log(cls, func: Callable[[T], T]): - """Function used to log a function each time it is run""" - if cls.DEBUG > 0: - return func - - def wrapper(cleaning_object: T) -> T: - start_time = time() - new_object = func(cleaning_object) - elapsed_time = time() - start_time - changed = new_object != cleaning_object - cls._update_log(func, elapsed_time, changed) - return new_object - - return wrapper - - @classmethod - def _update_log(cls, func: Callable[[T], T], time_spent: float, success: bool): - """Function used to change log data""" - log_id = getattr(func, "log_id") - level = cls.LOG - if level == 0: - return - if level > 0: - cls.log_tracker["Global Tracker"][log_id]["Time Spent"] += round( - time_spent, 4 - ) - cls.log_tracker["Global Tracker"][log_id]["Attempts"] += 1 - cls.log_tracker["Global Tracker"][log_id]["Successes"] += int(success) - if level > 1: - cls.log_tracker[cls._currently_tracking][log_id]["Time Spent"] += round( - time_spent, 4 - ) - cls.log_tracker[cls._currently_tracking][log_id]["Attempts"] += 1 - cls.log_tracker[cls._currently_tracking][log_id]["Successes"] += int( - success - ) - - @classmethod - def _debug(cls, func: Callable[[T], T]): - """Sets the debug behavior for cleaning functions.""" - if cls.DEBUG > 0: - - def wrapper(cleaning_object: T) -> T: - start_time = time() - new_object = func(cleaning_object) - elapsed_time = time() - start_time - changed = new_object != cleaning_object - cls._update_log(func, elapsed_time, changed) - if changed: - if cls.DEBUG > 1: - - print( - f"++ {cls.__name__}.{func.__name__} elapsed time : {elapsed_time} ++" - ) - old_counts = cleaning_object.initial_conditions(2) - new_counts = new_object.initial_conditions(2) - assert old_counts == new_counts, ( - f"Counts differ after {cls.__name__}.{func.__name__}:" - + f"\nInitial counts: {old_counts}\nClean counts: {new_counts}" - + f"\n {cleaning_object}\n {new_object}" - + f"\n {repr(cleaning_object)}" - ) - elif cls.DEBUG > 1: - print( - f"-- {cls.__name__}.{func.__name__} elapsed time : {elapsed_time} --" - ) - return new_object - - return wrapper - return func + def status_update(cls) -> str: + """Gives the full status update for all cleaner instances""" + logs = list(log for log in cls.all_loggers if log.log_level > 0) + if not logs: + return f"Logging dissabled for all \n{cls.__name__} cleaners.\n" + logs.sort(key=lambda log: log.total_times, reverse=True) + all_tables = (logger.display() for logger in logs) + return f"{cls.__name__} Status:\n " + "\n".join(all_tables) + "\n" diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 65e7db5..51f78d6 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -12,9 +12,11 @@ from mapplings import MappedTiling, Parameter, ParameterList -from .cleaner import GenericCleaner, Register +from .cleaner import GenericCleaner, Register, CleanerLog from .parameter_cleaner import ParamCleaner +default_param_cleaner = ParamCleaner.make_full_cleaner("Param Default Cleaner") + class MTCleaner(GenericCleaner[MappedTiling]): """The cleaner for mapped tilings. @@ -23,6 +25,10 @@ class MTCleaner(GenericCleaner[MappedTiling]): DEBUG = 0 reg = Register[MappedTiling]() + global_tracker = CleanerLog[MappedTiling]( + reg.registered_functions, name="Global Tracker" + ) + all_loggers = {global_tracker} # Final Methods @staticmethod @@ -72,7 +78,7 @@ def _clean_parameters(mappling: MappedTiling) -> MappedTiling: @reg(6) def fully_clean_parameters(mappling: MappedTiling) -> MappedTiling: """Applies all parameter cleanning functions to all parameters""" - return MTCleaner.clean_parameters(ParamCleaner.make_full_cleaner())(mappling) + return MTCleaner.clean_parameters(default_param_cleaner)(mappling) @staticmethod @reg(0) diff --git a/mapplings/cleaners/parameter_cleaner.py b/mapplings/cleaners/parameter_cleaner.py index 6e11f10..18132a2 100644 --- a/mapplings/cleaners/parameter_cleaner.py +++ b/mapplings/cleaners/parameter_cleaner.py @@ -6,7 +6,7 @@ from gridded_cayley_permutations import Tiling from mapplings import Parameter -from .cleaner import GenericCleaner, Register +from .cleaner import GenericCleaner, Register, CleanerLog class ParamCleaner(GenericCleaner[Parameter]): @@ -20,6 +20,10 @@ class ParamCleaner(GenericCleaner[Parameter]): run_on_containers=True, run_on_enumerators=True, ) + global_tracker = CleanerLog[Parameter]( + reg.registered_functions, name="Global Tracker" + ) + all_loggers = {global_tracker} # Final Methods @staticmethod diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index f9050b3..7ca5db7 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -42,14 +42,14 @@ from .verification_strategy import NoParameterVerificationStrategy -MTCleaner.toggle_log(1) +MTCleaner.global_log_toggle(2) temp = CombinatorialSpecificationSearcher.status def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" output = ( - temp(self, elaborate) + MTCleaner.display_log() + ParamCleaner.display_log() + temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() ) return output @@ -62,9 +62,16 @@ class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): A strategy for placing requirements in a mapped tiling. """ + cleaner = MTCleaner.make_full_cleaner("Requirement Placement Cleaner") + def algorithm(self, tiling): return MTRequirementPlacement(tiling) + def decomposition_function(self, comb_class): + return tuple( + map(self.__class__.cleaner, super().decomposition_function(comb_class)) + ) + class MapplingPointPlacementFactory(PointPlacementFactory): """ @@ -224,6 +231,7 @@ def __init__( possibly_empty: bool = True, workable: bool = True, ): + self.cleaner = MTCleaner.make_full_cleaner() super().__init__( ignore_parent=ignore_parent, inferrable=inferrable, @@ -232,7 +240,7 @@ def __init__( ) def decomposition_function(self, comb_class): - return (MTCleaner.full_cleanup(comb_class),) + return (self.cleaner(comb_class),) def formal_step(self) -> str: return "Clean mappling" @@ -253,19 +261,24 @@ class MapplingFactorStrategy(FactorStrategy): A strategy for finding factors in a mapped tiling. """ + cleaner = MTCleaner.make_full_cleaner("Factoring Cleaner") + def decomposition_function(self, comb_class) -> tuple[MappedTiling, ...]: factors = Factor(comb_class).find_factors() if len(factors) <= 1: raise StrategyDoesNotApply + factors = tuple(map(self.__class__.cleaner, factors)) return factors class MapplingLessThanRowColSeparationStrategy(LessThanRowColSeparationStrategy): """A strategy for separating rows and columns with less than constraints.""" + cleaner = MTCleaner.make_full_cleaner("LT Separation Cleaner") + def decomposition_function(self, comb_class): algo = LTRowColSeparationMT(comb_class) - return (next(algo.separate()),) + return (self.__class__.cleaner(next(algo.separate())),) class MapplingLessThanOrEqualRowColSeparationStrategy( @@ -273,9 +286,11 @@ class MapplingLessThanOrEqualRowColSeparationStrategy( ): """A strategy for separating rows and columns with less than or equal constraints.""" + cleaner = MTCleaner.make_full_cleaner("LEQ Separation Cleaner") + def decomposition_function(self, comb_class): algo = LTORERowColSeparationMT(comb_class) - return tuple(algo.separate()) + return tuple(map(self.__class__.cleaner, algo.separate())) class MappedTileScopePack(StrategyPack): @@ -322,7 +337,7 @@ def point_placement(cls, rootmt): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -347,7 +362,7 @@ def row_placement(cls, rootmt): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -372,7 +387,7 @@ def col_placement(cls, rootmt): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -397,7 +412,7 @@ def row_and_col_placement(cls, rootmt): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -424,7 +439,7 @@ def point_row_and_col_placement(cls, rootmt): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/playground/cleaner_motzkin.py b/playground/cleaner_motzkin.py index 7bc0b31..0ef2dac 100644 --- a/playground/cleaner_motzkin.py +++ b/playground/cleaner_motzkin.py @@ -1,10 +1,14 @@ from cayley_permutations import CayleyPermutation from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from mapplings import MappedTiling, Parameter +from mapplings.cleaners import MTCleaner from gridded_cayley_permutations.row_col_map import RowColMap from mapplings.strategies import MappedTileScopePack from comb_spec_searcher import CombinatorialSpecificationSearcher + + + til = MappedTiling.from_vincular(CayleyPermutation([0, 1, 2]), []) ghost = til.delete_rows([4]) avoiding_parameters = [ @@ -27,5 +31,5 @@ pack = MappedTileScopePack.point_placement(mappling) searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=False) -spec = searcher.auto_search(status_update=30) +spec = searcher.auto_search(status_update=10) spec.show() From a7a967a6463d41fc458c808914d9892afa1de416 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:32:01 +0000 Subject: [PATCH 07/38] Update from Main --- mapplings/strategies/mapped_tilescope.py | 22 ++++++++++---------- mapplings/strategies/tilescope_strategies.py | 3 --- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 6b2c38a..1e21e77 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -16,7 +16,7 @@ MapplingVerticalInsertionEncodingPlacementFactory, MapplingHorizontalInsertionEncodingRequirementInsertionFactory, MapplingHorizontalInsertionEncodingPlacementFactory, - CleaningStrategy, + # CleaningStrategy, MapplingFactorStrategy, MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, @@ -41,7 +41,7 @@ def no_param_ver_point_placement(cls): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -70,7 +70,7 @@ def no_param_ver_row_and_col_placement(cls): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -101,7 +101,7 @@ def point_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -131,7 +131,7 @@ def row_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -161,7 +161,7 @@ def col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -191,7 +191,7 @@ def row_and_col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -223,7 +223,7 @@ def point_row_and_col_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -254,7 +254,7 @@ def vertical_insertion_encoding(cls): MapplingFactorStrategy(), MapplingVerticalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[CleaningStrategy()], + inferral_strats=[], # Removed Cleaning Strategy expansion_strats=[[MapplingVerticalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Vertical Insertion Encoding", @@ -272,7 +272,7 @@ def horizontal_insertion_encoding(cls): MapplingFactorStrategy(), MapplingHorizontalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[CleaningStrategy()], + inferral_strats=[], # Removed Cleaning Strategy expansion_strats=[[MapplingHorizontalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Horizontal Insertion Encoding", @@ -293,7 +293,7 @@ def insertion_row_and_col_placement(cls, rootmt: MappedTiling): CellInsertionFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 221beb3..0dccf9d 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -24,9 +24,7 @@ DIR_RIGHT, ) from comb_spec_searcher import ( - StrategyPack, DisjointUnionStrategy, - AtomStrategy, CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply @@ -39,7 +37,6 @@ LTRowColSeparationMT, ) from mapplings.cleaners import MTCleaner, ParamCleaner -from .verification_strategy import NoParameterVerificationStrategy MTCleaner.global_log_toggle(2) From 2c56c72915b166ca03ead833f4458bf7168d5555 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:49:44 +0000 Subject: [PATCH 08/38] tox? trying to find tox errors --- mapplings/cleaners/cleaner.py | 7 +++---- mapplings/strategies/tilescope_strategies.py | 21 ++++++++++---------- playground/cleaner_motzkin.py | 2 -- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 32f65d0..1ea180a 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -111,7 +111,6 @@ def __init__( self.log_level = log_level self.debug_level = debug_level self.functions = logged_functions - print(logged_functions) self.tracker = { getattr(func, "log_id"): { "Attempts": 0, @@ -147,6 +146,7 @@ def wrap_functions( def display(self) -> str: """Returns a string to display cleaner log data""" + # pylint: disable=too-many-locals if self.log_level == 0: return f"\n{self.name} logging is disabled \n" headers = [ @@ -305,8 +305,8 @@ class GenericCleaner(Generic[T]): DEBUG = 2 checks counts and elapsed time after each function LOG = 0 Skips logging - LOG = 1 Enables a global log - LOG = 2 Enables a detailed log + LOG = 1 Displays info per cleaner instance + LOG = 2 Displays info per function per cleaner instance DEBUG and LOG can be changed globally with global toggle functions or per instance with cleaner_instance.LOG = # or cleaner_instance.DEBUG = # @@ -445,7 +445,6 @@ def full_cleanup(cls, cleaning_object: T) -> T: def global_log_toggle(cls, level: int) -> None: """Updates the log level of all cleaner instances and resets tracking""" cls.LOG = level - print("RESET") for logger in cls.all_loggers: logger.log_level = level logger.tracker = logger.reset_log() diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 0dccf9d..aa72c6f 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -25,7 +25,7 @@ ) from comb_spec_searcher import ( DisjointUnionStrategy, - CombinatorialSpecificationSearcher, + # CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply from cayley_permutations import CayleyPermutation @@ -36,22 +36,21 @@ LTORERowColSeparationMT, LTRowColSeparationMT, ) -from mapplings.cleaners import MTCleaner, ParamCleaner +from mapplings.cleaners import MTCleaner # , ParamCleaner -MTCleaner.global_log_toggle(2) -temp = CombinatorialSpecificationSearcher.status +# temp = CombinatorialSpecificationSearcher.status -def new_status(self, elaborate: bool) -> str: - """Overwrites CSS status method""" - output = ( - temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() - ) - return output +# def new_status(self, elaborate: bool) -> str: +# """Overwrites CSS status method""" +# output = ( +# temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() +# ) +# return output -CombinatorialSpecificationSearcher.status = new_status +# CombinatorialSpecificationSearcher.status = new_status class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): diff --git a/playground/cleaner_motzkin.py b/playground/cleaner_motzkin.py index 0ef2dac..826a7b0 100644 --- a/playground/cleaner_motzkin.py +++ b/playground/cleaner_motzkin.py @@ -7,8 +7,6 @@ from comb_spec_searcher import CombinatorialSpecificationSearcher - - til = MappedTiling.from_vincular(CayleyPermutation([0, 1, 2]), []) ghost = til.delete_rows([4]) avoiding_parameters = [ From a2a545cbca58d9af6936b93da137e2f7f6424f55 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:03:28 +0000 Subject: [PATCH 09/38] Tox Again? --- mapplings/cleaners/cleaner.py | 2 +- mapplings/strategies/mapped_tilescope.py | 4 ++-- mapplings/strategies/tilescope_strategies.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 1ea180a..18cf860 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -90,7 +90,7 @@ def __str__(self): funcs = sorted(self.registered_functions, key=self.sorting_key) output = f"{funcs[0].__qualname__.split('.')[0]} has registered the following functions:" for func in funcs: - output += f"\n{getattr(func, "index")} : {func.__name__}" + output += f"\n{getattr(func, 'index')} : {func.__name__}" for key, value in tuple(func.__dict__.items())[1:]: output += f"\n -{key}={value}" output += "\n" diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 1e21e77..f6a23bb 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -16,7 +16,7 @@ MapplingVerticalInsertionEncodingPlacementFactory, MapplingHorizontalInsertionEncodingRequirementInsertionFactory, MapplingHorizontalInsertionEncodingPlacementFactory, - # CleaningStrategy, + CleaningStrategy, MapplingFactorStrategy, MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, @@ -101,7 +101,7 @@ def point_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), # this is here for tests MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index aa72c6f..cfec03e 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -39,6 +39,7 @@ from mapplings.cleaners import MTCleaner # , ParamCleaner +# MTCleaner.global_log_toggle(2) # temp = CombinatorialSpecificationSearcher.status From 54df492c863dea4fcab2e33a2ca4e484efb253ab Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:25:58 +0000 Subject: [PATCH 10/38] Various Fixes Made tox work, dealt with unregistered functions. --- mapplings/cleaners/cleaner.py | 32 +++++++++++++++++--- mapplings/cleaners/mappling_cleaner.py | 2 +- mapplings/strategies/mapped_tilescope.py | 4 +-- mapplings/strategies/tilescope_strategies.py | 24 +++++++-------- playground/cleaner_motzkin.py | 4 +-- playground/inc_inc.py | 2 +- 6 files changed, 46 insertions(+), 22 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 18cf860..1c4c96d 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -100,6 +100,7 @@ def __str__(self): class CleanerLog(Generic[T]): """A class for tracking cleaners""" + # pylint: disable=too-many-instance-attributes def __init__( self, logged_functions: Iterable[Callable[[T], T]], @@ -122,10 +123,25 @@ def __init__( } self.global_tracker: "CleanerLog" | None = None self.total_times = [0.0, 0.0] + self.runs = 0 + self.changes_made = 0 def __call__(self, func: Callable[[T], T]): return self._debug(self._log(func)) + def add_function(self, func: Callable[[T], T]): + """Adds a function to the tracker""" + self.tracker.update( + { + getattr(func, "log_id"): { + "Attempts": 0, + "Successes": 0, + "Success Time": 0.0, + "Fail Time": 0.0, + } + } + ) + def reset_log(self) -> dict[str, dict[str, int | float]]: """Using this to set LOG and reset debug tracker""" return { @@ -214,8 +230,8 @@ def display(self) -> str: table = [ ( self.name, - total_attempt, - total_success, + self.runs, + self.changes_made, f"{int(attempt_ratio)}%", timedelta(seconds=int(self.total_times[1])), timedelta(seconds=int(self.total_times[0])), @@ -252,11 +268,15 @@ def _update_log(self, func: Callable[[T], T], time_spent: float, success: bool): key = "Fail Time" if self.global_tracker is not None: if self.global_tracker.log_level > 0: + if log_id not in self.global_tracker.tracker: + self.global_tracker.add_function(func) self.global_tracker.tracker[log_id][key] += round(time_spent, 4) self.global_tracker.tracker[log_id]["Attempts"] += 1 self.global_tracker.tracker[log_id]["Successes"] += int(success) self.global_tracker.total_times[success] += time_spent if self.log_level > 0: + if log_id not in self.tracker: + self.add_function(func) self.tracker[log_id][key] += round(time_spent, 4) self.tracker[log_id]["Attempts"] += 1 self.tracker[log_id]["Successes"] += int(success) @@ -395,7 +415,11 @@ def loop_cleanup( + f"\n{cleaning_object}\n {new_cleaning_object}" + f"\n{repr(cleaning_object)}" ) - + if cls.LOG > 0: + cls._currently_tracking.runs += 1 + cls._currently_tracking.changes_made += int(iterations > 0) + cls.global_tracker.runs += 1 + cls.global_tracker.changes_made += int(iterations > 0) return new_cleaning_object @classmethod @@ -461,7 +485,7 @@ def status_update(cls) -> str: """Gives the full status update for all cleaner instances""" logs = list(log for log in cls.all_loggers if log.log_level > 0) if not logs: - return f"Logging dissabled for all \n{cls.__name__} cleaners.\n" + return f"Logging dissabled for all {cls.__name__} cleaners.\n" logs.sort(key=lambda log: log.total_times, reverse=True) all_tables = (logger.display() for logger in logs) return f"{cls.__name__} Status:\n " + "\n".join(all_tables) + "\n" diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 51f78d6..9ea44bf 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -40,7 +40,7 @@ def clean_parameters( To apply to a mappling, can be run as MTCleaner.make_param_cleaner(param_cleaner)(mappling) """ - @MTCleaner.reg(6, update_register=False) + @MTCleaner.reg(6, update_register=False, log_id="Clean Parameters") def _clean_parameters(mappling: MappedTiling) -> MappedTiling: temp = mappling.apply_to_all_parameters( Parameter.update_active_cells, (mappling.tiling,) diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index f6a23bb..9f7479d 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -16,7 +16,7 @@ MapplingVerticalInsertionEncodingPlacementFactory, MapplingHorizontalInsertionEncodingRequirementInsertionFactory, MapplingHorizontalInsertionEncodingPlacementFactory, - CleaningStrategy, + # CleaningStrategy, MapplingFactorStrategy, MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, @@ -101,7 +101,7 @@ def point_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), # this is here for tests + # CleaningStrategy(), # this is here for tests MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index cfec03e..41d9ef0 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -25,7 +25,7 @@ ) from comb_spec_searcher import ( DisjointUnionStrategy, - # CombinatorialSpecificationSearcher, + CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply from cayley_permutations import CayleyPermutation @@ -36,22 +36,22 @@ LTORERowColSeparationMT, LTRowColSeparationMT, ) -from mapplings.cleaners import MTCleaner # , ParamCleaner +from mapplings.cleaners import MTCleaner, ParamCleaner -# MTCleaner.global_log_toggle(2) -# temp = CombinatorialSpecificationSearcher.status +MTCleaner.global_log_toggle(1) +temp = CombinatorialSpecificationSearcher.status -# def new_status(self, elaborate: bool) -> str: -# """Overwrites CSS status method""" -# output = ( -# temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() -# ) -# return output +def new_status(self, elaborate: bool) -> str: + """Overwrites CSS status method""" + output = ( + temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() + ) + return output -# CombinatorialSpecificationSearcher.status = new_status +CombinatorialSpecificationSearcher.status = new_status class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): @@ -228,7 +228,7 @@ def __init__( possibly_empty: bool = True, workable: bool = True, ): - self.cleaner = MTCleaner.make_full_cleaner() + self.cleaner = MTCleaner.make_full_cleaner("Cleaner Strategy") super().__init__( ignore_parent=ignore_parent, inferrable=inferrable, diff --git a/playground/cleaner_motzkin.py b/playground/cleaner_motzkin.py index 826a7b0..64409b1 100644 --- a/playground/cleaner_motzkin.py +++ b/playground/cleaner_motzkin.py @@ -7,7 +7,7 @@ from comb_spec_searcher import CombinatorialSpecificationSearcher -til = MappedTiling.from_vincular(CayleyPermutation([0, 1, 2]), []) +til = MappedTiling.from_vincular_with_obs(CayleyPermutation([0, 1, 2]), []) ghost = til.delete_rows([4]) avoiding_parameters = [ Parameter(ghost, RowColMap({i: 0 for i in range(7)}, {i: 0 for i in range(6)})) @@ -25,7 +25,7 @@ [], [], ) - +mappling = MTCleaner.list_cleanup(mappling, MTCleaner.reg.registered_functions) pack = MappedTileScopePack.point_placement(mappling) searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=False) diff --git a/playground/inc_inc.py b/playground/inc_inc.py index d07c16d..0c1ca71 100644 --- a/playground/inc_inc.py +++ b/playground/inc_inc.py @@ -2,7 +2,7 @@ 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.tilescope_strategies import MappedTileScopePack +from mapplings.strategies.mapped_tilescope import MappedTileScopePack from comb_spec_searcher import CombinatorialSpecificationSearcher ghost = Tiling( From 213f71f6aa9b2777f4c768dc736176e292d421f3 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:14:16 +0000 Subject: [PATCH 11/38] Brought Back Cleaning Strategy I'm confused as to why this is necessary, but here we are. --- .tox/py312/.tox-info.json | 6 ++++++ mapplings/strategies/mapped_tilescope.py | 22 ++++++++++---------- mapplings/strategies/tilescope_strategies.py | 10 +++++---- 3 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 .tox/py312/.tox-info.json diff --git a/.tox/py312/.tox-info.json b/.tox/py312/.tox-info.json new file mode 100644 index 0000000..5c62a33 --- /dev/null +++ b/.tox/py312/.tox-info.json @@ -0,0 +1,6 @@ +{ + "ToxEnv": { + "name": "py312", + "type": "VirtualEnvRunner" + } +} \ No newline at end of file diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 9f7479d..6b2c38a 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -16,7 +16,7 @@ MapplingVerticalInsertionEncodingPlacementFactory, MapplingHorizontalInsertionEncodingRequirementInsertionFactory, MapplingHorizontalInsertionEncodingPlacementFactory, - # CleaningStrategy, + CleaningStrategy, MapplingFactorStrategy, MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, @@ -41,7 +41,7 @@ def no_param_ver_point_placement(cls): MapplingPointPlacementFactory(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -70,7 +70,7 @@ def no_param_ver_row_and_col_placement(cls): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -101,7 +101,7 @@ def point_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - # CleaningStrategy(), # this is here for tests + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -131,7 +131,7 @@ def row_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -161,7 +161,7 @@ def col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -191,7 +191,7 @@ def row_and_col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -223,7 +223,7 @@ def point_row_and_col_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -254,7 +254,7 @@ def vertical_insertion_encoding(cls): MapplingFactorStrategy(), MapplingVerticalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[], # Removed Cleaning Strategy + inferral_strats=[CleaningStrategy()], expansion_strats=[[MapplingVerticalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Vertical Insertion Encoding", @@ -272,7 +272,7 @@ def horizontal_insertion_encoding(cls): MapplingFactorStrategy(), MapplingHorizontalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[], # Removed Cleaning Strategy + inferral_strats=[CleaningStrategy()], expansion_strats=[[MapplingHorizontalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Horizontal Insertion Encoding", @@ -293,7 +293,7 @@ def insertion_row_and_col_placement(cls, rootmt: MappedTiling): CellInsertionFactory(), ], inferral_strats=[ - # CleaningStrategy(), + CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 41d9ef0..fe70481 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -221,6 +221,8 @@ class CleaningStrategy(DisjointUnionStrategy[MappedTiling, GriddedCayleyPerm]): A strategy for cleaning a mapped tiling. """ + cleaner = MTCleaner.make_full_cleaner("Cleaner Strategy") + def __init__( self, ignore_parent: bool = True, @@ -228,7 +230,7 @@ def __init__( possibly_empty: bool = True, workable: bool = True, ): - self.cleaner = MTCleaner.make_full_cleaner("Cleaner Strategy") + super().__init__( ignore_parent=ignore_parent, inferrable=inferrable, @@ -237,7 +239,7 @@ def __init__( ) def decomposition_function(self, comb_class): - return (self.cleaner(comb_class),) + return (self.__class__.cleaner(comb_class),) def formal_step(self) -> str: return "Clean mappling" @@ -275,7 +277,7 @@ class MapplingLessThanRowColSeparationStrategy(LessThanRowColSeparationStrategy) def decomposition_function(self, comb_class): algo = LTRowColSeparationMT(comb_class) - return (self.__class__.cleaner(next(algo.separate())),) + return tuple(map(self.__class__.cleaner, algo.separate())) class MapplingLessThanOrEqualRowColSeparationStrategy( @@ -287,4 +289,4 @@ class MapplingLessThanOrEqualRowColSeparationStrategy( def decomposition_function(self, comb_class): algo = LTORERowColSeparationMT(comb_class) - return tuple(algo.separate()) + return tuple(map(self.__class__.cleaner, algo.separate())) From 943e062a4158f10ed18ab381246722c450343f18 Mon Sep 17 00:00:00 2001 From: Reed Acton <110858904+ReedActon@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:25:33 +0000 Subject: [PATCH 12/38] Delete .tox/py312/.tox-info.json --- .tox/py312/.tox-info.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .tox/py312/.tox-info.json diff --git a/.tox/py312/.tox-info.json b/.tox/py312/.tox-info.json deleted file mode 100644 index 5c62a33..0000000 --- a/.tox/py312/.tox-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "ToxEnv": { - "name": "py312", - "type": "VirtualEnvRunner" - } -} \ No newline at end of file From 8b7c1e5b7043251a5b8198bce5f845f62fb04778 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:12:27 +0000 Subject: [PATCH 13/38] Update tox.ini to include types-tabulate Add types-tabulate to mypy dependencies --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index d17b4e0..8de40f9 100644 --- a/tox.ini +++ b/tox.ini @@ -52,6 +52,7 @@ description = run mypy (static type checker) basepython = {[default]basepython} deps = mypy==1.15.0 + types-tabulate commands = mypy mapplings [testenv:black] From 1528527e8b7f81197504905e71d2f6dfd157a386 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:16:00 +0000 Subject: [PATCH 14/38] Cast new_status to Callable type --- mapplings/strategies/tilescope_strategies.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index fe70481..7b19f47 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -1,6 +1,6 @@ """Strategies for mapplings tilescope.""" -from typing import Iterator +from typing import Iterator, cast, Callable from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( @@ -51,7 +51,10 @@ def new_status(self, elaborate: bool) -> str: return output -CombinatorialSpecificationSearcher.status = new_status +CombinatorialSpecificationSearcher.status = cast( + Callable[["CombinatorialSpecificationSearcher", bool], str], + new_status +) class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 70321feffc924e6a351e7a92c1a5dd5e8cc8f6d8 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:19:54 +0000 Subject: [PATCH 15/38] Fix type hint for CombinatorialSpecificationSearcher --- mapplings/strategies/tilescope_strategies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 7b19f47..e821760 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -28,6 +28,7 @@ CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply +from comb_spec_searcher.typing import CombinatorialClassType from cayley_permutations import CayleyPermutation from mapplings import MappedTiling from mapplings.algorithms import ( @@ -52,7 +53,7 @@ def new_status(self, elaborate: bool) -> str: CombinatorialSpecificationSearcher.status = cast( - Callable[["CombinatorialSpecificationSearcher", bool], str], + Callable[[CombinatorialSpecificationSearcher[CombinatorialClassType], bool], str], new_status ) From 51f600285ebcfc16f7f29beec781fc17b0822407 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:25:18 +0000 Subject: [PATCH 16/38] Refactor status casting in CombinatorialSpecificationSearcher --- mapplings/strategies/tilescope_strategies.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index e821760..01c9683 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -28,7 +28,6 @@ CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply -from comb_spec_searcher.typing import CombinatorialClassType from cayley_permutations import CayleyPermutation from mapplings import MappedTiling from mapplings.algorithms import ( @@ -52,10 +51,7 @@ def new_status(self, elaborate: bool) -> str: return output -CombinatorialSpecificationSearcher.status = cast( - Callable[[CombinatorialSpecificationSearcher[CombinatorialClassType], bool], str], - new_status -) +CombinatorialSpecificationSearcher.status = cast(Callable[[Any, bool], str], new_status) class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 04b4ca3fe40450df4a65350106b613ccf060d429 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:25:36 +0000 Subject: [PATCH 17/38] Add 'Any' type to import statements --- mapplings/strategies/tilescope_strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 01c9683..084a69b 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -1,6 +1,6 @@ """Strategies for mapplings tilescope.""" -from typing import Iterator, cast, Callable +from typing import Iterator, cast, Callable, Any from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( From c623f0fb74d7a18680e2bead3a2c453f5dc3b506 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:32:25 +0000 Subject: [PATCH 18/38] Refactor new_status method for improved readability --- mapplings/strategies/tilescope_strategies.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 084a69b..08de9b8 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -45,13 +45,16 @@ def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" - output = ( - temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() + return ( + temp(self, elaborate) + + MTCleaner.status_update() + + ParamCleaner.status_update() ) - return output -CombinatorialSpecificationSearcher.status = cast(Callable[[Any, bool], str], new_status) +CombinatorialSpecificationSearcher.status = cast( + Callable[[Any, bool], str], new_status +) # type: ignore[method-assign] class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 8119360378a67d23fb9256fd8e8224ec02a526b2 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:36:54 +0000 Subject: [PATCH 19/38] Refactor new_status method for cleaner return --- mapplings/strategies/tilescope_strategies.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 08de9b8..159e94a 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -45,16 +45,13 @@ def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" - return ( - temp(self, elaborate) - + MTCleaner.status_update() - + ParamCleaner.status_update() - ) + return temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() + CombinatorialSpecificationSearcher.status = cast( Callable[[Any, bool], str], new_status -) # type: ignore[method-assign] +) # type: ignore[method-assign,assignment] class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 70f77411c6a9dc5746c89753499959585b52b22e Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:41:39 +0000 Subject: [PATCH 20/38] Update tilescope_strategies.py --- mapplings/strategies/tilescope_strategies.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 159e94a..4588ff0 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -46,12 +46,11 @@ def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" return temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() - -CombinatorialSpecificationSearcher.status = cast( - Callable[[Any, bool], str], new_status -) # type: ignore[method-assign,assignment] +CombinatorialSpecificationSearcher.status = ( # type: ignore[method-assign] + cast(Callable[[Any, bool]], new_status) +) class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 6beec10c3d2a4c383597a727394c28c529a77626 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:44:06 +0000 Subject: [PATCH 21/38] Fix type casting for new_status in status assignment --- mapplings/strategies/tilescope_strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 4588ff0..3838400 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -49,7 +49,7 @@ def new_status(self, elaborate: bool) -> str: CombinatorialSpecificationSearcher.status = ( # type: ignore[method-assign] - cast(Callable[[Any, bool]], new_status) + cast(Callable[[Any, bool], str], new_status) ) From dcc5c172a03145b831efab28496524ff9a657679 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:45:08 +0000 Subject: [PATCH 22/38] Update tilescope_strategies.py --- mapplings/strategies/tilescope_strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 3838400..da65060 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -48,7 +48,7 @@ def new_status(self, elaborate: bool) -> str: return temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() -CombinatorialSpecificationSearcher.status = ( # type: ignore[method-assign] +CombinatorialSpecificationSearcher.status = ( # type: ignore[method-assign,assignment] cast(Callable[[Any, bool], str], new_status) ) From 21833ac46b1527193f10341ce6f6e13884894c9f Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:49:09 +0000 Subject: [PATCH 23/38] Refactor new_status method for better readability --- mapplings/strategies/tilescope_strategies.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index da65060..7a00c91 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -45,11 +45,13 @@ def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" - return temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() + return ( + temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() + ) -CombinatorialSpecificationSearcher.status = ( # type: ignore[method-assign,assignment] - cast(Callable[[Any, bool], str], new_status) +CombinatorialSpecificationSearcher.status = ( + cast(Callable[[Any, bool], str], new_status) # type: ignore ) From c2c247d0ca9d5f09598a6ecb7c8383f8fce46d0e Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:51:19 +0000 Subject: [PATCH 24/38] Fix formatting of status assignment in tilescope_strategies.py --- mapplings/strategies/tilescope_strategies.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 7a00c91..6dec562 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -51,8 +51,8 @@ def new_status(self, elaborate: bool) -> str: CombinatorialSpecificationSearcher.status = ( - cast(Callable[[Any, bool], str], new_status) # type: ignore -) + cast(Callable[[Any, bool], str], new_status) +) # type: ignore class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 3079b56daa0f439392c8ae76271e2710829fc777 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:53:36 +0000 Subject: [PATCH 25/38] Fix type ignore comment for status assignment --- mapplings/strategies/tilescope_strategies.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 6dec562..93db688 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -50,9 +50,9 @@ def new_status(self, elaborate: bool) -> str: ) -CombinatorialSpecificationSearcher.status = ( +CombinatorialSpecificationSearcher.status = ( # type: ignore cast(Callable[[Any, bool], str], new_status) -) # type: ignore +) class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From 6e5a6720d8a5629ac701db11cffd8a9b8ea302f6 Mon Sep 17 00:00:00 2001 From: Christian Bean Date: Thu, 4 Dec 2025 13:55:00 +0000 Subject: [PATCH 26/38] Refactor status assignment in CombinatorialSpecificationSearcher --- mapplings/strategies/tilescope_strategies.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 93db688..9640fa3 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -50,9 +50,7 @@ def new_status(self, elaborate: bool) -> str: ) -CombinatorialSpecificationSearcher.status = ( # type: ignore - cast(Callable[[Any, bool], str], new_status) -) +CombinatorialSpecificationSearcher.status = new_status # type: ignore class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): From c7865e8ab19409b16e3534dcfad4554b456cf3b2 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 13:59:05 +0000 Subject: [PATCH 27/38] Display Improvements and Strategy Overwrites Made the display prettier and added mappling specific strategies. --- mapplings/cleaners/cleaner.py | 74 +++++++++++--------- mapplings/strategies/mapped_tilescope.py | 32 ++++----- mapplings/strategies/tilescope_strategies.py | 56 +++++++++++---- 3 files changed, 99 insertions(+), 63 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index 1c4c96d..fb95a00 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -1,6 +1,7 @@ """Module with the generic cleaner and register classes""" from typing import TypeVar, Callable, Generic, Iterable +from itertools import chain from time import time from datetime import timedelta from tabulate import tabulate, SEPARATING_LINE @@ -160,35 +161,16 @@ def wrap_functions( """Applies the debug and log wrappers to each function""" return (self._debug(self._log(func)) for func in functions) - def display(self) -> str: + def display( + self, + ) -> list[tuple[str, int, int, str, timedelta, timedelta, timedelta, str]] | str: """Returns a string to display cleaner log data""" # pylint: disable=too-many-locals if self.log_level == 0: return f"\n{self.name} logging is disabled \n" - headers = [ - "", - "\nAttempts", - "\nSuccesses", - "Success\n Rate", - "Time Spent\n on Successes", - "Time Spent\n on Failures", - "Total\nElapsed Time", - "Percent of\n Total Time", - ] - coalign = ( - "left", - "right", - "right", - "right", - "right", - "right", - "right", - "right", - ) + table = list[tuple[str, int, int, str, timedelta, timedelta, timedelta, str]]() rows = dict[str, float]() - total_attempt = 0 - total_success = 0 total_time = sum(self.total_times) if self.global_tracker is None: time_ratio = 100.0 @@ -200,11 +182,9 @@ def display(self) -> str: time_ratio = total_time / global_time * 100 for name, record in self.tracker.items(): ftime = record["Success Time"] + record["Fail Time"] - rows.update(((f" {name}", ftime),)) + rows.update(((f"++{name}", ftime),)) attempt = int(record["Attempts"]) success = int(record["Successes"]) - total_attempt += attempt - total_success += success if self.log_level > 1: if total_time == 0: ftime_ratio = 0 @@ -212,7 +192,7 @@ def display(self) -> str: ftime_ratio = int((ftime / total_time) * 100) table.append( ( - f" {name}", + f"++{name}", attempt, success, f"{int((success / (max(attempt, attempt == 0))) * 100)}%", @@ -223,11 +203,12 @@ def display(self) -> str: ) ) table.sort(key=lambda row: rows[row[0]], reverse=True) - if total_attempt == 0: + if self.runs == 0: attempt_ratio = 0.0 else: - attempt_ratio = (total_success / total_attempt) * 100 + attempt_ratio = (self.changes_made / self.runs) * 100 table = [ + SEPARATING_LINE, ( self.name, self.runs, @@ -240,8 +221,8 @@ def display(self) -> str: ), SEPARATING_LINE, ] + table - - return "\n" + tabulate(table, headers=headers, colalign=coalign) + return table + # return "\n" + tabulate(table, headers=headers, colalign=coalign) def _log(self, func: Callable[[T], T]): """Function used to log a function each time it is run""" @@ -483,9 +464,34 @@ def global_debug_toggle(cls, level: int) -> None: @classmethod def status_update(cls) -> str: """Gives the full status update for all cleaner instances""" - logs = list(log for log in cls.all_loggers if log.log_level > 0) + headers = [ + "", + "\nAttempts", + "\nSuccesses", + "Success\nRate", + "Time Spent\non Successes", + "Time Spent\non Failures", + "Total\nElapsed Time", + "Percent of\nTotal Time", + ] + coalign = ( + "left", + "right", + "right", + "right", + "right", + "right", + "right", + "right", + ) + logs = list( + log for log in cls.all_loggers if log.log_level > 0 and log.runs > 0 + ) if not logs: return f"Logging dissabled for all {cls.__name__} cleaners.\n" logs.sort(key=lambda log: log.total_times, reverse=True) - all_tables = (logger.display() for logger in logs) - return f"{cls.__name__} Status:\n " + "\n".join(all_tables) + "\n" + all_tables = chain.from_iterable((logger.display() for logger in logs)) + table_display = tabulate(all_tables, headers=headers, colalign=coalign).replace( + "++", " " + ) + return f"{cls.__name__} Status:\n{table_display}\n" diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 6b2c38a..24a51fd 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -1,6 +1,5 @@ """Different strategy packs for MappedTileScope.""" -from tilescope.strategies import CellInsertionFactory from comb_spec_searcher import StrategyPack, AtomStrategy from mapplings import MappedTiling from .verification_strategy import ( @@ -16,10 +15,11 @@ MapplingVerticalInsertionEncodingPlacementFactory, MapplingHorizontalInsertionEncodingRequirementInsertionFactory, MapplingHorizontalInsertionEncodingPlacementFactory, - CleaningStrategy, + # CleaningStrategy, MapplingFactorStrategy, MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, + MapplingCellInsertionFactory, ) @@ -41,12 +41,12 @@ def no_param_ver_point_placement(cls): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ [ - CellInsertionFactory(), + MapplingCellInsertionFactory(), ] ], ver_strats=[ @@ -70,7 +70,7 @@ def no_param_ver_row_and_col_placement(cls): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -101,12 +101,12 @@ def point_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ [ - CellInsertionFactory(), + MapplingCellInsertionFactory(), ] ], ver_strats=[ @@ -131,7 +131,7 @@ def row_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -161,7 +161,7 @@ def col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -191,7 +191,7 @@ def row_and_col_placement(cls, rootmt: MappedTiling): MapplingLessThanOrEqualRowColSeparationStrategy(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ @@ -223,12 +223,12 @@ def point_row_and_col_placement(cls, rootmt: MappedTiling): MapplingPointPlacementFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ [ - CellInsertionFactory(), + MapplingCellInsertionFactory(), MapplingRowPlacementFactory(), MapplingColPlacementFactory(), ] @@ -254,7 +254,7 @@ def vertical_insertion_encoding(cls): MapplingFactorStrategy(), MapplingVerticalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[CleaningStrategy()], + inferral_strats=[], # CleaningStrategy() expansion_strats=[[MapplingVerticalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Vertical Insertion Encoding", @@ -272,7 +272,7 @@ def horizontal_insertion_encoding(cls): MapplingFactorStrategy(), MapplingHorizontalInsertionEncodingRequirementInsertionFactory(), ], - inferral_strats=[CleaningStrategy()], + inferral_strats=[], # CleaningStrategy() expansion_strats=[[MapplingHorizontalInsertionEncodingPlacementFactory()]], ver_strats=[AtomStrategy()], name="Horizontal Insertion Encoding", @@ -290,10 +290,10 @@ def insertion_row_and_col_placement(cls, rootmt: MappedTiling): initial_strats=[ MapplingFactorStrategy(), MapplingLessThanOrEqualRowColSeparationStrategy(), - CellInsertionFactory(), + MapplingCellInsertionFactory(), ], inferral_strats=[ - CleaningStrategy(), + # CleaningStrategy(), MapplingLessThanRowColSeparationStrategy(), ], expansion_strats=[ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 6dec562..13152d4 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -45,14 +45,13 @@ def new_status(self, elaborate: bool) -> str: """Overwrites CSS status method""" - return ( + output = ( temp(self, elaborate) + MTCleaner.status_update() + ParamCleaner.status_update() ) + return output -CombinatorialSpecificationSearcher.status = ( - cast(Callable[[Any, bool], str], new_status) -) # type: ignore +CombinatorialSpecificationSearcher.status = cast(Callable[[Any, bool], str], new_status) class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): @@ -60,7 +59,7 @@ class MapplingRequirementPlacementStrategy(RequirementPlacementStrategy): A strategy for placing requirements in a mapped tiling. """ - cleaner = MTCleaner.make_full_cleaner("Requirement Placement Cleaner") + cleaner = MTCleaner.make_full_cleaner("Req Placement Cleaner") def algorithm(self, tiling): return MTRequirementPlacement(tiling) @@ -71,6 +70,29 @@ def decomposition_function(self, comb_class): ) +class MapplingRequirementInsertionStrategy(RequirementInsertionStrategy): + """Mappling version of RequirementInsertionStrategy with a cleaner""" + + cleaner = MTCleaner.make_full_cleaner("Req Insertion Cleaner") + + def decomposition_function(self, comb_class): + return tuple( + map(self.__class__.cleaner, super().decomposition_function(comb_class)) + ) + + +class MapplingCellInsertionFactory(CellInsertionFactory): + """Factory for inserting points into active cells of a tiling.""" + + def __call__( + self, comb_class: Tiling + ) -> Iterator[MapplingRequirementInsertionStrategy]: + for cell in comb_class.active_cells: + gcps = (GriddedCayleyPerm(CayleyPermutation([0]), (cell,)),) + strategy = MapplingRequirementInsertionStrategy(gcps, ignore_parent=False) + yield strategy + + class MapplingPointPlacementFactory(PointPlacementFactory): """ A factory for creating point placement strategies for mapped tilings. @@ -91,7 +113,9 @@ def __call__( class MapplingRowPlacementFactory(RowInsertionFactory): """A factory for placing the minimum points in the rows of tilings.""" - def __call__(self, comb_class: Tiling) -> Iterator[RequirementPlacementStrategy]: + def __call__( + self, comb_class: Tiling + ) -> Iterator[MapplingRequirementPlacementStrategy]: not_point_rows = set(range(comb_class.dimensions[1])) - comb_class.point_rows for row in not_point_rows: all_gcps = [] @@ -125,18 +149,20 @@ def __call__(self, comb_class: Tiling) -> Iterator[RequirementPlacementStrategy] class MapplingVerticalInsertionEncodingRequirementInsertionFactory( - CellInsertionFactory + MapplingCellInsertionFactory ): """A factory for making columns positive in mapplings for vertical insertion encoding.""" - def __call__(self, comb_class: Tiling) -> Iterator[RequirementInsertionStrategy]: + def __call__( + self, comb_class: Tiling + ) -> Iterator[MapplingRequirementInsertionStrategy]: for col in range(comb_class.dimensions[0]): if not comb_class.col_is_positive(col): gcps = tuple( GriddedCayleyPerm(CayleyPermutation([0]), [cell]) for cell in comb_class.cells_in_col(col) ) - yield RequirementInsertionStrategy(gcps, ignore_parent=True) + yield MapplingRequirementInsertionStrategy(gcps, ignore_parent=True) return @classmethod @@ -152,7 +178,9 @@ def __str__(self) -> str: class MapplingVerticalInsertionEncodingPlacementFactory(MapplingRowPlacementFactory): """A factory for placing the bottom leftmost points in mapplings.""" - def __call__(self, comb_class: Tiling) -> Iterator[RequirementPlacementStrategy]: + def __call__( + self, comb_class: Tiling + ) -> Iterator[MapplingRequirementPlacementStrategy]: cells = comb_class.active_cells gcps = tuple( GriddedCayleyPerm(CayleyPermutation([0]), [cell]) for cell in cells @@ -170,18 +198,20 @@ def __str__(self) -> str: class MapplingHorizontalInsertionEncodingRequirementInsertionFactory( - CellInsertionFactory + MapplingCellInsertionFactory ): """A factory for making rows positive in mapplings for horizontal insertion encoding.""" - def __call__(self, comb_class: Tiling) -> Iterator[RequirementInsertionStrategy]: + def __call__( + self, comb_class: Tiling + ) -> Iterator[MapplingRequirementInsertionStrategy]: for row in range(comb_class.dimensions[1]): if not comb_class.row_is_positive(row): gcps = tuple( GriddedCayleyPerm(CayleyPermutation([0]), [cell]) for cell in comb_class.cells_in_row(row) ) - yield RequirementInsertionStrategy(gcps, ignore_parent=True) + yield MapplingRequirementInsertionStrategy(gcps, ignore_parent=True) @classmethod def from_dict( From 3419e65ae889a004e41ed64125e271d8a460d678 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:49:54 +0000 Subject: [PATCH 28/38] Tox and Display Final display changes and tox --- mapplings/cleaners/cleaner.py | 31 ++++++++++++-------- mapplings/strategies/tilescope_strategies.py | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/mapplings/cleaners/cleaner.py b/mapplings/cleaners/cleaner.py index fb95a00..b5e23c5 100644 --- a/mapplings/cleaners/cleaner.py +++ b/mapplings/cleaners/cleaner.py @@ -1,6 +1,6 @@ """Module with the generic cleaner and register classes""" -from typing import TypeVar, Callable, Generic, Iterable +from typing import TypeVar, Callable, Generic, Iterable, Sequence from itertools import chain from time import time from datetime import timedelta @@ -163,12 +163,12 @@ def wrap_functions( def display( self, - ) -> list[tuple[str, int, int, str, timedelta, timedelta, timedelta, str]] | str: + ) -> list[ + tuple[str, int, int, str, timedelta, timedelta, timedelta, str] + | Sequence[str | int | timedelta] + ]: """Returns a string to display cleaner log data""" # pylint: disable=too-many-locals - if self.log_level == 0: - return f"\n{self.name} logging is disabled \n" - table = list[tuple[str, int, int, str, timedelta, timedelta, timedelta, str]]() rows = dict[str, float]() total_time = sum(self.total_times) @@ -207,7 +207,7 @@ def display( attempt_ratio = 0.0 else: attempt_ratio = (self.changes_made / self.runs) * 100 - table = [ + final_table = [ SEPARATING_LINE, ( self.name, @@ -221,7 +221,7 @@ def display( ), SEPARATING_LINE, ] + table - return table + return final_table # return "\n" + tabulate(table, headers=headers, colalign=coalign) def _log(self, func: Callable[[T], T]): @@ -254,14 +254,12 @@ def _update_log(self, func: Callable[[T], T], time_spent: float, success: bool): self.global_tracker.tracker[log_id][key] += round(time_spent, 4) self.global_tracker.tracker[log_id]["Attempts"] += 1 self.global_tracker.tracker[log_id]["Successes"] += int(success) - self.global_tracker.total_times[success] += time_spent if self.log_level > 0: if log_id not in self.tracker: self.add_function(func) self.tracker[log_id][key] += round(time_spent, 4) self.tracker[log_id]["Attempts"] += 1 self.tracker[log_id]["Successes"] += int(success) - self.total_times[success] += time_spent def _debug(self, func: Callable[[T], T]): """Sets the debug behavior for cleaning functions.""" @@ -384,7 +382,8 @@ def loop_cleanup( old_cleaning_object = new_cleaning_object new_cleaning_object = cls.list_cleanup(old_cleaning_object, cleaning_list) continue_cleaning = old_cleaning_object != new_cleaning_object - if cls.DEBUG > 0: + changes_made = iterations > 0 + if cls._currently_tracking.debug_level > 0: print( f"Cleaned in {iterations} loops. Elapsed time : {time() - start_time}" ) @@ -396,11 +395,17 @@ def loop_cleanup( + f"\n{cleaning_object}\n {new_cleaning_object}" + f"\n{repr(cleaning_object)}" ) - if cls.LOG > 0: + if cls._currently_tracking.log_level > 0: cls._currently_tracking.runs += 1 - cls._currently_tracking.changes_made += int(iterations > 0) + cls._currently_tracking.changes_made += int(changes_made) + cls._currently_tracking.total_times[changes_made] += round( + time() - start_time, 4 + ) cls.global_tracker.runs += 1 - cls.global_tracker.changes_made += int(iterations > 0) + cls.global_tracker.changes_made += int(changes_made) + cls.global_tracker.total_times[changes_made] += round( + time() - start_time, 4 + ) return new_cleaning_object @classmethod diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 109a992..b61b191 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -1,6 +1,6 @@ """Strategies for mapplings tilescope.""" -from typing import Iterator, cast, Callable, Any +from typing import Iterator from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( From 28df247ee8543009ad4bd6b510eb14f5d123d9d4 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:01:41 +0000 Subject: [PATCH 29/38] Add IL Factoring Strategies --- mapplings/strategies/tilescope_strategies.py | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index b61b191..ce03fec 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -5,6 +5,7 @@ from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( FactorStrategy, + ShuffleFactorStrategy, RequirementPlacementStrategy, LessThanRowColSeparationStrategy, LessThanOrEqualRowColSeparationStrategy, @@ -33,6 +34,8 @@ from mapplings.algorithms import ( MTRequirementPlacement, Factor, + ILFactorNormal, + ILFactorInverted, LTORERowColSeparationMT, LTRowColSeparationMT, ) @@ -301,6 +304,42 @@ def decomposition_function(self, comb_class) -> tuple[MappedTiling, ...]: return factors +class MapplingILFactorStrategy(ShuffleFactorStrategy): + """ + A strategy for finding interleaving factors in a mapped tiling. + """ + + cleaner = MTCleaner.make_full_cleaner("IL Factoring Cleaner") + + def decomposition_function(self, comb_class) -> tuple[MappedTiling, ...]: + factors = ILFactorNormal(comb_class).find_factors() + if len(factors) <= 1: + raise StrategyDoesNotApply + factors = tuple(map(self.__class__.cleaner, factors)) + return factors + + def formal_step(self) -> str: + return "Factor the mappling into interleaving factors" + + +class MapplingInvertedILFactorStrategy(FactorStrategy): + """ + A strategy for finding interleaving factors in a mapped tiling by inverting 00 obstructions + """ + + cleaner = MTCleaner.make_full_cleaner("Inverted IL Factoring Cleaner") + + def decomposition_function(self, comb_class) -> tuple[MappedTiling, ...]: + factors = ILFactorInverted(comb_class).find_factors() + if len(factors) <= 1: + raise StrategyDoesNotApply + factors = tuple(map(self.__class__.cleaner, factors)) + return factors + + def formal_step(self) -> str: + return "Invert obstructions and factor the mappling into interleaving factors" + + class MapplingLessThanRowColSeparationStrategy(LessThanRowColSeparationStrategy): """A strategy for separating rows and columns with less than constraints.""" From 857954721bf1accc6669634067b6e81a32ddccd3 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:03:53 +0000 Subject: [PATCH 30/38] Update tilescope_strategies.py --- mapplings/strategies/tilescope_strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index ce03fec..6674f28 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -322,7 +322,7 @@ def formal_step(self) -> str: return "Factor the mappling into interleaving factors" -class MapplingInvertedILFactorStrategy(FactorStrategy): +class MapplingInvertedILFactorStrategy(ShuffleFactorStrategy): """ A strategy for finding interleaving factors in a mapped tiling by inverting 00 obstructions """ From 56f6fec82e6d661fb9c3b0e852cf76f898cf7011 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:11:23 +0000 Subject: [PATCH 31/38] New Parameter Strategies Added param placement and param insertion --- mapplings/algorithms/__init__.py | 3 +- mapplings/algorithms/parmeter_placement.py | 175 +++++++++++++++++++ mapplings/strategies/tilescope_strategies.py | 69 +++++++- playground/param_placement_play.py | 31 ++++ 4 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 mapplings/algorithms/parmeter_placement.py create mode 100644 playground/param_placement_play.py diff --git a/mapplings/algorithms/__init__.py b/mapplings/algorithms/__init__.py index b50dedc..47aef10 100644 --- a/mapplings/algorithms/__init__.py +++ b/mapplings/algorithms/__init__.py @@ -3,7 +3,7 @@ from .factor import Factor, ILFactorInverted, ILFactorNormal from .row_col_sep_mt import LTRowColSeparationMT, LTORERowColSeparationMT from .point_placement import MTRequirementPlacement - +from .parmeter_placement import ParameterPlacement __all__ = [ "Factor", @@ -12,5 +12,6 @@ "LTRowColSeparationMT", "LTORERowColSeparationMT", "MTRequirementPlacement", + "ParameterPlacement", ] __version__ = "0.1.0" diff --git a/mapplings/algorithms/parmeter_placement.py b/mapplings/algorithms/parmeter_placement.py new file mode 100644 index 0000000..cabdb51 --- /dev/null +++ b/mapplings/algorithms/parmeter_placement.py @@ -0,0 +1,175 @@ +"""Algorithm for parameter placement""" + +from cayley_permutations import CayleyPermutation + +from gridded_cayley_permutations import GriddedCayleyPerm, Tiling +from gridded_cayley_permutations.point_placements import PointPlacement +from gridded_cayley_permutations.row_col_map import RowColMap + +from mapplings import MappedTiling, Parameter, ParameterList +from .point_placement import MTRequirementPlacement + + +Cell = tuple[int, int] + + +class ParameterPlacement: + """For a given mappling and containing parameter, places the parameter + in a cell of the tiling of the mappling. Places the point of the parameter + at the given index (0 based) in the given direction.""" + + def __init__(self, mappling: MappedTiling, param_list: ParameterList) -> None: + """cell is the cell in the tiling which the parameter will be placed into""" + assert len(param_list) == 1, "Too many Parameters in ParameterList." + assert ( + param_list in mappling.containing_parameters + ), "ParameterList does not exist." + self.mappling = mappling + new_containers = list(mappling.containing_parameters) + new_containers.remove(param_list) + self.adjusted_mappling = MappedTiling( + mappling.tiling, + mappling.avoiding_parameters, + new_containers, + mappling.enumerating_parameters, + ) + self.param = tuple(param_list)[0] + + def param_placement(self, direction: int, index_of_pattern: int) -> MappedTiling: + """Place a parameter in the tiling. + index_of_pattern is the index of the pattern that is placed in the tiling and is 0 based. + """ + param_cell = tuple(self.param.point_cells())[index_of_pattern] + cell = (self.param.col_map[param_cell[0]], self.param.col_map[param_cell[1]]) + new_mappling = MTRequirementPlacement( + self.adjusted_mappling + ).directionless_point_placement(cell) + new_avoiding_parameters = list( + new_mappling.avoiding_parameters + ) + self.find_new_avoiding_parameters(direction, cell, param_cell) + + new_containing_parameters = self.update_containing_parameters( + param_cell, cell, new_mappling.containing_parameters + ) + new_base = new_mappling.tiling + algo = PointPlacement(self.mappling.tiling) + point_obs, point_reqs = algo.point_obstructions_and_requirements(cell) + new_obs = new_base.obstructions + point_obs + new_reqs = new_base.requirements + point_reqs + new_base = Tiling(new_obs, new_reqs, new_base.dimensions) + return MappedTiling( + new_base, + new_avoiding_parameters, + new_containing_parameters, + new_mappling.enumerating_parameters, + ) + + def find_new_avoiding_parameters( + self, direction: int, base_cell: Cell, param_cell: Cell + ): + """Return a list of new avoiding parameters for the new mappling.""" + cells_to_insert_in = self.cells_to_insert_point_in( + direction, base_cell, param_cell + ) + new_avoiding_parameters = [] + for cell in cells_to_insert_in: + new_ghost = PointPlacement(self.param.ghost).directionless_point_placement( + cell + ) + new_avoiding_parameters.append( + MTRequirementPlacement( + self.mappling + ).new_parameter_from_point_placed_tiling(self.param, new_ghost, cell) + ) + return new_avoiding_parameters + + def cells_in_parameter(self, base_cell: Cell) -> tuple[Cell, ...]: + """Gets the preimage of the base cell according to the param map.""" + return self.param.map.preimage_of_cell(base_cell) + + def cells_to_insert_point_in( + self, direction: int, base_cell: Cell, param_cell: Cell + ): + """Returns a list of cells in the parameter which a point can be + placed into for the resulting tiling to be an avoiding parameter (cells + which are not further in the given direction so that the pattern in the + parameter will be, therefore it must be avoided.)""" + all_cells = [ + cell + for cell in self.cells_in_parameter(base_cell) + if GriddedCayleyPerm(CayleyPermutation([0]), [cell]) + not in self.param.ghost.obstructions + ] + cell_of_point_being_placed = param_cell + cells_to_insert_in = [ + cell + for cell in all_cells + if PointPlacement(self.param.ghost).farther( + cell_of_point_being_placed, cell, direction + ) + ] + return cells_to_insert_in + + def update_containing_parameters( + self, + param_cell: Cell, + base_cell: Cell, + containing_parameters: tuple[ParameterList, ...], + ) -> tuple[ParameterList, ...]: + """Remove [self.param] from containing parameters and add new + containing parameter list (one that is the identity).""" + param_to_update = Parameter(self.param.ghost, self.param.map) + point_placed_ghost = PointPlacement( + param_to_update.ghost + ).directionless_point_placement(param_cell) + new_map = self.new_containing_param_map( + param_cell, base_cell, point_placed_ghost + ) + new_containing_param = Parameter(point_placed_ghost, new_map) + return (ParameterList((new_containing_param,)),) + containing_parameters + + def new_containing_param_map( + self, param_cell: Cell, base_cell: Cell, ghost: Tiling + ): + """Return a new RowColMap for the containing parameter that was placed.""" + new_row_map = self.adjust_dict_in_param( + param_cell, base_cell, True, ghost.dimensions[1] + ) + new_col_map = self.adjust_dict_in_param( + param_cell, base_cell, False, ghost.dimensions[0] + ) + return RowColMap(new_col_map, new_row_map) + + def adjust_dict_in_param( + self, + middle_cell: Cell, + base_cell: Cell, + adjust_rows: bool, + dimension: int, + ): + """Update a row or col map given the cell in the new parameter where the point is placed + and the old row or col map. + If adjust_rows = 0 then it returns the new col map, if 1 then it returns the new row map. + """ + if adjust_rows: + new_map = self.param.row_map + else: + new_map = self.param.col_map + vals_in_param = set( + cell[adjust_rows] for cell in self.cells_in_parameter(base_cell) + ) + middle_val = middle_cell[adjust_rows] + 1 + for val in range(dimension): + if val not in vals_in_param: + if val > middle_val: + if val not in new_map: + new_map[val] = base_cell[adjust_rows] + 2 + else: + new_map[val] += 2 + elif val < middle_val: + new_map[val] = base_cell[adjust_rows] + elif val > middle_val: + new_map[val] = base_cell[adjust_rows] + 2 + else: + new_map[val] = base_cell[adjust_rows] + 1 + return new_map diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 6674f28..3b4f312 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -1,6 +1,6 @@ """Strategies for mapplings tilescope.""" -from typing import Iterator +from typing import Iterator, Optional from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( @@ -29,10 +29,12 @@ CombinatorialSpecificationSearcher, ) from comb_spec_searcher.exception import StrategyDoesNotApply +from comb_spec_searcher.strategies.constructor import DisjointUnion, Complement from cayley_permutations import CayleyPermutation -from mapplings import MappedTiling +from mapplings import MappedTiling, Parameter, ParameterList from mapplings.algorithms import ( MTRequirementPlacement, + ParameterPlacement, Factor, ILFactorNormal, ILFactorInverted, @@ -96,6 +98,69 @@ def __call__( yield strategy +class ParameterPlacementStrategy(MapplingRequirementPlacementStrategy): + + cleaner = MTCleaner.make_full_cleaner("Param Placement Cleaner") + + def __init__(self, c_list_index: int, point_index: int, direction: int): + self.c_list_index = c_list_index + self.index = point_index + self.direction = direction + + def algorithm(self, mappling: MappedTiling): + c_list = mappling.containing_parameters[self.c_list_index] + return ParameterPlacement(mappling, c_list) + + def decomposition_function(self, comb_class): + new_mappling = self.algorithm(comb_class).param_placement( + self.direction, self.index + ) + return (self.__class__.cleaner(new_mappling),) + + +class ParamPlacementFactory(PointPlacementFactory): + def __call__( + self, comb_class: MappedTiling + ) -> Iterator[ParameterPlacementStrategy]: + for c_index, c_list in enumerate(comb_class.containing_parameters): + if len(c_list) != 1: + continue + param = tuple(c_list)[0] + points = tuple(param.point_cells()) + if not points: + continue + for i in range(points): + for direction in Directions: + yield ParameterPlacementStrategy(c_index, i, direction) + + +class ParameterInsertionStrategy(DisjointUnionStrategy): + """Straregy for inserting parameters""" + + def __init__( + self, + params: ParameterList, + ignore_parent=False, + inferrable=True, + possibly_empty=True, + workable=True, + ): + self.params = params + super().__init__(ignore_parent, inferrable, possibly_empty, workable) + + def decomposition_function(self, comb_class: MappedTiling): + avoiders, containers, enumerators = comb_class.ace_parameters() + base = comb_class.tiling + new_avoiders = ParameterList(avoiders | self.params) + new_containers = list(containers) + [self.params] + m1 = MappedTiling(base, new_avoiders, containers, enumerators) + m2 = MappedTiling(base, avoiders, new_containers, enumerators) + return (m1, m2) + + def formal_step(self): + return "Contain or avoid parameters" + + class MapplingPointPlacementFactory(PointPlacementFactory): """ A factory for creating point placement strategies for mapped tilings. diff --git a/playground/param_placement_play.py b/playground/param_placement_play.py new file mode 100644 index 0000000..223ad96 --- /dev/null +++ b/playground/param_placement_play.py @@ -0,0 +1,31 @@ +from cayley_permutations import CayleyPermutation +from gridded_cayley_permutations import Tiling, GriddedCayleyPerm +from mapplings import MappedTiling, Parameter, ParameterList +from mapplings.algorithms import ParameterPlacement +from mapplings.cleaners import MTCleaner +from gridded_cayley_permutations.row_col_map import RowColMap +from mapplings.strategies.mapped_tilescope import MappedTileScopePack +from comb_spec_searcher import CombinatorialSpecificationSearcher + +ghost = Tiling.from_vincular_with_obs(CayleyPermutation((0,)), []) +ghost = ghost.add_obstructions( + ( + GriddedCayleyPerm((0,), [(0, 2)]), + GriddedCayleyPerm((0,), [(2, 0)]), + GriddedCayleyPerm((0,), [(0, 1)]), + GriddedCayleyPerm((0,), [(2, 1)]), + ) +) +P = Parameter(ghost, RowColMap({0: 0, 1: 0, 2: 0}, {0: 0, 1: 0, 2: 0})) +B = Tiling([GriddedCayleyPerm((0, 0), [(0, 0), (0, 0)])], [], (1, 1)) + +c_list = ParameterList([P]) +mappling = MappedTiling(B, [], [c_list], []) +mappling = MTCleaner.full_cleanup(mappling) +c_list = mappling.containing_parameters[0] +print(mappling) + +mappling = MTCleaner.full_cleanup( + ParameterPlacement(mappling, c_list).param_placement(0, 0) +) +print(mappling) From fd9d5b9b0dbdfa64cd3b2f2cceee8fe8f45ddea6 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:25:19 +0000 Subject: [PATCH 32/38] Strategy Updates Added stretegies to get the cursed tree --- mapplings/algorithms/parmeter_placement.py | 4 +- mapplings/strategies/mapped_tilescope.py | 30 +++ mapplings/strategies/tilescope_strategies.py | 234 ++++++++++++++++++- playground/param_placement_play.py | 15 +- 4 files changed, 262 insertions(+), 21 deletions(-) diff --git a/mapplings/algorithms/parmeter_placement.py b/mapplings/algorithms/parmeter_placement.py index cabdb51..e1445ee 100644 --- a/mapplings/algorithms/parmeter_placement.py +++ b/mapplings/algorithms/parmeter_placement.py @@ -152,9 +152,9 @@ def adjust_dict_in_param( If adjust_rows = 0 then it returns the new col map, if 1 then it returns the new row map. """ if adjust_rows: - new_map = self.param.row_map + new_map = self.param.row_map.copy() else: - new_map = self.param.col_map + new_map = self.param.col_map.copy() vals_in_param = set( cell[adjust_rows] for cell in self.cells_in_parameter(base_cell) ) diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 24a51fd..a66a917 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -20,6 +20,8 @@ MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, MapplingCellInsertionFactory, + ParamPlacementFactory, + AvoiderExorcismFactory ) @@ -312,3 +314,31 @@ def insertion_row_and_col_placement(cls, rootmt: MappedTiling): symmetries=[], iterative=False, ) + + @classmethod + def parameter_tomfoolery(cls, rootmt: MappedTiling): + """ + Create a row and column placement strategy pack which initially + makes all cells positive. + """ + return MappedTileScopePack( + initial_strats=[ + MapplingFactorStrategy() + ], + inferral_strats=[], + expansion_strats=[ + [ + ParamPlacementFactory(), + AvoiderExorcismFactory(), + ] + ], + ver_strats=[ + AtomStrategy(), + NoParameterVerificationStrategy(rootmt), + # MapplingVerticalInsertionEncodableVerificationStrategy(), + # MapplingHorizontalInsertionEncodableVerificationStrategy(), + ], + name="Param Nonsense", + symmetries=[], + iterative=False, + ) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 3b4f312..5b5229f 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -27,11 +27,13 @@ from comb_spec_searcher import ( DisjointUnionStrategy, CombinatorialSpecificationSearcher, + StrategyFactory, ) from comb_spec_searcher.exception import StrategyDoesNotApply from comb_spec_searcher.strategies.constructor import DisjointUnion, Complement +from comb_spec_searcher.strategies.rule import Rule from cayley_permutations import CayleyPermutation -from mapplings import MappedTiling, Parameter, ParameterList +from mapplings import MappedTiling, ParameterList from mapplings.algorithms import ( MTRequirementPlacement, ParameterPlacement, @@ -44,7 +46,7 @@ from mapplings.cleaners import MTCleaner, ParamCleaner -MTCleaner.global_log_toggle(1) +MTCleaner.global_debug_toggle(0) temp = CombinatorialSpecificationSearcher.status @@ -98,16 +100,25 @@ def __call__( yield strategy -class ParameterPlacementStrategy(MapplingRequirementPlacementStrategy): +class ParameterPlacementStrategy(DisjointUnionStrategy): + """Strategy for placing parameters""" cleaner = MTCleaner.make_full_cleaner("Param Placement Cleaner") - def __init__(self, c_list_index: int, point_index: int, direction: int): + def __init__( + self, + c_list_index: int, + point_index: int, + direction: int, + ignore_parent: bool = False, + ): self.c_list_index = c_list_index self.index = point_index self.direction = direction + super().__init__(ignore_parent) def algorithm(self, mappling: MappedTiling): + """Algorithm used by the decomposition function""" c_list = mappling.containing_parameters[self.c_list_index] return ParameterPlacement(mappling, c_list) @@ -117,8 +128,58 @@ def decomposition_function(self, comb_class): ) return (self.__class__.cleaner(new_mappling),) + def formal_step(self): + return ( + f"Placed point {self.index} of containing parameter {self.c_list_index} " + + f"in direction {self.direction}" + ) + + def backward_map( + self, + comb_class: MappedTiling, + objs: tuple[Optional[GriddedCayleyPerm], ...], + children: Optional[tuple[MappedTiling, ...]] = None, + ) -> Iterator[GriddedCayleyPerm]: + raise NotImplementedError + + def forward_map( + self, + comb_class: MappedTiling, + obj: GriddedCayleyPerm, + children: Optional[tuple[MappedTiling, ...]] = None, + ) -> tuple[Optional[GriddedCayleyPerm], ...]: + raise NotImplementedError + + def to_jsonable(self) -> dict: + """Return a dictionary form of the strategy.""" + d: dict = super().to_jsonable() + d.pop("workable") + d.pop("inferrable") + d.pop("possibly_empty") + d["c_list_index"] = self.c_list_index + d["point_index"] = self.index + d["direction"] = self.direction + return d + + @classmethod + def from_dict(cls, d: dict) -> "ParameterPlacementStrategy": + """Return a strategy from a dictionary.""" + return cls(**d) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(" + + f"{self.c_list_index}, {self.index}, {self.direction}" + + f"ignore_parent={self.ignore_parent})" + ) + + def __str__(self): + return "Placed a parameter" + + +class ParamPlacementFactory(StrategyFactory[MappedTiling]): + """Tries to place the points of any size 1 containing parameter list""" -class ParamPlacementFactory(PointPlacementFactory): def __call__( self, comb_class: MappedTiling ) -> Iterator[ParameterPlacementStrategy]: @@ -129,37 +190,188 @@ def __call__( points = tuple(param.point_cells()) if not points: continue - for i in range(points): + for i in range(len(points)): for direction in Directions: yield ParameterPlacementStrategy(c_index, i, direction) + + @classmethod + def from_dict(cls, d: dict) -> "ParamPlacementFactory": + return cls(**d) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}()" + + def __str__(self) -> str: + return "Parameter placement" class ParameterInsertionStrategy(DisjointUnionStrategy): """Straregy for inserting parameters""" + cleaner = MTCleaner.make_full_cleaner("Param Insertion Cleaner") + def __init__( self, params: ParameterList, ignore_parent=False, - inferrable=True, - possibly_empty=True, - workable=True, ): self.params = params - super().__init__(ignore_parent, inferrable, possibly_empty, workable) + super().__init__(ignore_parent) def decomposition_function(self, comb_class: MappedTiling): avoiders, containers, enumerators = comb_class.ace_parameters() base = comb_class.tiling new_avoiders = ParameterList(avoiders | self.params) new_containers = list(containers) + [self.params] + clean = self.__class__.cleaner m1 = MappedTiling(base, new_avoiders, containers, enumerators) m2 = MappedTiling(base, avoiders, new_containers, enumerators) - return (m1, m2) + return (clean(m1), clean(m2)) def formal_step(self): return "Contain or avoid parameters" + def backward_map( + self, + comb_class: MappedTiling, + objs: tuple[Optional[GriddedCayleyPerm], ...], + children: Optional[tuple[MappedTiling, ...]] = None, + ) -> Iterator[GriddedCayleyPerm]: + raise NotImplementedError + + def forward_map( + self, + comb_class: MappedTiling, + obj: GriddedCayleyPerm, + children: Optional[tuple[MappedTiling, ...]] = None, + ) -> tuple[Optional[GriddedCayleyPerm], ...]: + raise NotImplementedError + + def to_jsonable(self) -> dict: + """Return a dictionary form of the strategy.""" + d: dict = super().to_jsonable() + d.pop("workable") + d.pop("inferrable") + d.pop("possibly_empty") + d["params"] = self.params + return d + + @classmethod + def from_dict(cls, d: dict) -> "ParameterInsertionStrategy": + """Return a strategy from a dictionary.""" + return cls(**d) + + +class AvoiderExorcismStrategy(ParameterInsertionStrategy): + """Strategy for transforming Avoiders into Containers""" + + cleaner = MTCleaner.make_full_cleaner("Avoider Exorcism Cleaner") + + def __init__(self, params, ignore_parent=False): + assert len(params) == 1 + super().__init__(params, ignore_parent) + + def __call__( + self, + comb_class: MappedTiling, + children: Optional[tuple[MappedTiling, MappedTiling]] = None, + ) -> Rule: + if children is None: + children = self.decomposition_function(comb_class) + if children is None: + raise StrategyDoesNotApply("Strategy does not apply") + return Rule(self, children[0], (comb_class, children[1])) + + def decomposition_function(self, comb_class): + avoider = tuple(self.params)[0] + avoiders, containers, enumerators = comb_class.ace_parameters() + assert avoider in avoiders, "Avoider Does Not Exist" + base = comb_class.tiling + new_avoiders = list(avoiders) + new_avoiders.remove(avoider) + new_containers = list(containers) + [self.params] + clean = self.__class__.cleaner + m1 = MappedTiling(base, new_avoiders, containers, enumerators) + m2 = MappedTiling(base, new_avoiders, new_containers, enumerators) + return (clean(m1), clean(m2)) + + def constructor( + self, + comb_class: MappedTiling, + children: Optional[tuple[MappedTiling, MappedTiling]] = None, + ) -> DisjointUnion: + if children is None: + children = self.decomposition_function(comb_class) + if children is None: + raise StrategyDoesNotApply("Strategy does not apply") + return DisjointUnion(children[0], (comb_class, children[1])) + + def reverse_constructor( + self, + idx: int, + comb_class: MappedTiling, + children: Optional[tuple[MappedTiling, MappedTiling]] = None, + ): + if children is None: + children = self.decomposition_function(comb_class) + if children is None: + raise StrategyDoesNotApply("Strategy does not apply") + return Complement(children[0], (comb_class, children[1]), idx) + + +class ContainerExorcismStrategy(AvoiderExorcismStrategy): + """Strategy for transforming Containers into Avoiders""" + + cleaner = MTCleaner.make_full_cleaner("Container Exorcism Cleaner") + + def decomposition_function(self, comb_class: MappedTiling): + contianer = tuple(self.params)[0] + avoiders, containers, enumerators = comb_class.ace_parameters() + assert self.params in containers, "Container Does Not Exist" + base = comb_class.tiling + new_avoiders = avoiders.add(contianer) + new_containers = list(containers) + new_containers.remove(self.params) + clean = self.__class__.cleaner + m1 = MappedTiling(base, avoiders, new_containers, enumerators) + m2 = MappedTiling(base, new_avoiders, new_containers, enumerators) + return (clean(m1), clean(m2)) + + +class AvoiderExorcismFactory(StrategyFactory[MappedTiling]): + """Transforms avoiders which map to a single cell into containers""" + + def __call__(self, comb_class): + for avoider in comb_class.avoiding_parameters: + if len(set(avoider.col_map.values())) == len(set(avoider.row_map.values())) == 1: + yield AvoiderExorcismStrategy(ParameterList({avoider})) + + @classmethod + def from_dict(cls, d: dict) -> "AvoiderExorcismFactory": + return cls(**d) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}()" + + def __str__(self) -> str: + return "Avoider exorcism" + + +class ContainerExorcismFactory(StrategyFactory[MappedTiling]): + """Transforms solo containers which map to a single cell into avoiders""" + + def __call__(self, comb_class): + for c_list in comb_class.containing_parameters: + if len(c_list) > 1: + continue + for container in c_list: + if ( + len(container.col_map.values()) + == len(container.row_map.values()) + == 1 + ): + yield ContainerExorcismStrategy(ParameterList({container})) + class MapplingPointPlacementFactory(PointPlacementFactory): """ diff --git a/playground/param_placement_play.py b/playground/param_placement_play.py index 223ad96..03b4419 100644 --- a/playground/param_placement_play.py +++ b/playground/param_placement_play.py @@ -6,6 +6,7 @@ from gridded_cayley_permutations.row_col_map import RowColMap from mapplings.strategies.mapped_tilescope import MappedTileScopePack from comb_spec_searcher import CombinatorialSpecificationSearcher +from comb_spec_searcher.rule_db import RuleDBForest ghost = Tiling.from_vincular_with_obs(CayleyPermutation((0,)), []) ghost = ghost.add_obstructions( @@ -18,14 +19,12 @@ ) P = Parameter(ghost, RowColMap({0: 0, 1: 0, 2: 0}, {0: 0, 1: 0, 2: 0})) B = Tiling([GriddedCayleyPerm((0, 0), [(0, 0), (0, 0)])], [], (1, 1)) - +ruledb = RuleDBForest() c_list = ParameterList([P]) -mappling = MappedTiling(B, [], [c_list], []) +mappling = MappedTiling(B, [P], [], []) mappling = MTCleaner.full_cleanup(mappling) -c_list = mappling.containing_parameters[0] -print(mappling) +pack = MappedTileScopePack.parameter_tomfoolery(None) +searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=True, ruledb=ruledb) -mappling = MTCleaner.full_cleanup( - ParameterPlacement(mappling, c_list).param_placement(0, 0) -) -print(mappling) +spec = searcher.auto_search(status_update=10,) +spec.show() \ No newline at end of file From 612d39e6d58ac1ae1de2dd52640d1cefe3f5e9be Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 4 Dec 2025 22:18:53 +0000 Subject: [PATCH 33/38] tox Made tox happy --- mapplings/strategies/mapped_tilescope.py | 8 +++----- mapplings/strategies/tilescope_strategies.py | 14 +++++++++----- playground/param_placement_play.py | 6 ++++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index a66a917..77ab648 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -21,7 +21,7 @@ MapplingLessThanOrEqualRowColSeparationStrategy, MapplingCellInsertionFactory, ParamPlacementFactory, - AvoiderExorcismFactory + AvoiderExorcismFactory, ) @@ -314,7 +314,7 @@ def insertion_row_and_col_placement(cls, rootmt: MappedTiling): symmetries=[], iterative=False, ) - + @classmethod def parameter_tomfoolery(cls, rootmt: MappedTiling): """ @@ -322,9 +322,7 @@ def parameter_tomfoolery(cls, rootmt: MappedTiling): makes all cells positive. """ return MappedTileScopePack( - initial_strats=[ - MapplingFactorStrategy() - ], + initial_strats=[MapplingFactorStrategy()], inferral_strats=[], expansion_strats=[ [ diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 5b5229f..ab5f42c 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -172,7 +172,7 @@ def __repr__(self) -> str: + f"{self.c_list_index}, {self.index}, {self.direction}" + f"ignore_parent={self.ignore_parent})" ) - + def __str__(self): return "Placed a parameter" @@ -193,7 +193,7 @@ def __call__( for i in range(len(points)): for direction in Directions: yield ParameterPlacementStrategy(c_index, i, direction) - + @classmethod def from_dict(cls, d: dict) -> "ParamPlacementFactory": return cls(**d) @@ -270,7 +270,7 @@ class AvoiderExorcismStrategy(ParameterInsertionStrategy): def __init__(self, params, ignore_parent=False): assert len(params) == 1 super().__init__(params, ignore_parent) - + def __call__( self, comb_class: MappedTiling, @@ -343,9 +343,13 @@ class AvoiderExorcismFactory(StrategyFactory[MappedTiling]): def __call__(self, comb_class): for avoider in comb_class.avoiding_parameters: - if len(set(avoider.col_map.values())) == len(set(avoider.row_map.values())) == 1: + if ( + len(set(avoider.col_map.values())) + == len(set(avoider.row_map.values())) + == 1 + ): yield AvoiderExorcismStrategy(ParameterList({avoider})) - + @classmethod def from_dict(cls, d: dict) -> "AvoiderExorcismFactory": return cls(**d) diff --git a/playground/param_placement_play.py b/playground/param_placement_play.py index 03b4419..cb3c448 100644 --- a/playground/param_placement_play.py +++ b/playground/param_placement_play.py @@ -26,5 +26,7 @@ pack = MappedTileScopePack.parameter_tomfoolery(None) searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=True, ruledb=ruledb) -spec = searcher.auto_search(status_update=10,) -spec.show() \ No newline at end of file +spec = searcher.auto_search( + status_update=10, +) +spec.show() From 952cda877f7e6e077c1de038e585066e127cecca Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:29:19 +0000 Subject: [PATCH 34/38] Do it smart Made adjustments to avoider/container exorcism. Added ParameterInsertionFactory. --- mapplings/strategies/tilescope_strategies.py | 129 ++++++++----------- 1 file changed, 52 insertions(+), 77 deletions(-) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index e3309ea..3015fe3 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -1,6 +1,6 @@ """Strategies for mapplings tilescope.""" -from typing import Iterator, Optional +from typing import Iterator, Optional, Iterable from gridded_cayley_permutations import Tiling, GriddedCayleyPerm from gridded_cayley_permutations.point_placements import Directions from tilescope.strategies import ( @@ -30,10 +30,8 @@ StrategyFactory, ) from comb_spec_searcher.exception import StrategyDoesNotApply -from comb_spec_searcher.strategies.constructor import DisjointUnion, Complement -from comb_spec_searcher.strategies.rule import Rule from cayley_permutations import CayleyPermutation -from mapplings import MappedTiling, ParameterList +from mapplings import MappedTiling, ParameterList, Parameter from mapplings.algorithms import ( MTRequirementPlacement, ParameterPlacement, @@ -263,93 +261,51 @@ def from_dict(cls, d: dict) -> "ParameterInsertionStrategy": return cls(**d) -class AvoiderExorcismStrategy(ParameterInsertionStrategy): - """Strategy for transforming Avoiders into Containers""" +class ParameterInsertionFactory(StrategyFactory[MappedTiling]): + """Inserts special parameters to mapplings with 1x1 base tiling""" - cleaner = MTCleaner.make_full_cleaner("Avoider Exorcism Cleaner") - - def __init__(self, params, ignore_parent=False): - assert len(params) == 1 - super().__init__(params, ignore_parent) - - def __call__( - self, - comb_class: MappedTiling, - children: Optional[tuple[MappedTiling, MappedTiling]] = None, - ) -> Rule: - if children is None: - children = self.decomposition_function(comb_class) - if children is None: - raise StrategyDoesNotApply("Strategy does not apply") - return Rule(self, children[0], (comb_class, children[1])) - - def decomposition_function(self, comb_class): - avoider = tuple(self.params)[0] - avoiders, containers, enumerators = comb_class.ace_parameters() - assert avoider in avoiders, "Avoider Does Not Exist" - base = comb_class.tiling - new_avoiders = list(avoiders) - new_avoiders.remove(avoider) - new_containers = list(containers) + [self.params] - clean = self.__class__.cleaner - m1 = MappedTiling(base, new_avoiders, containers, enumerators) - m2 = MappedTiling(base, new_avoiders, new_containers, enumerators) - return (clean(m1), clean(m2)) - - def constructor( - self, - comb_class: MappedTiling, - children: Optional[tuple[MappedTiling, MappedTiling]] = None, - ) -> DisjointUnion: - if children is None: - children = self.decomposition_function(comb_class) - if children is None: - raise StrategyDoesNotApply("Strategy does not apply") - return DisjointUnion(children[0], (comb_class, children[1])) - - def reverse_constructor( - self, - idx: int, - comb_class: MappedTiling, - children: Optional[tuple[MappedTiling, MappedTiling]] = None, - ): - if children is None: - children = self.decomposition_function(comb_class) - if children is None: - raise StrategyDoesNotApply("Strategy does not apply") - return Complement(children[0], (comb_class, children[1]), idx) + def __init__(self, special_params: Iterable[Parameter]): + assert all( + (len(param.image_cells()) == 1 for param in special_params) + ), "Param cells must all map to (0,0)" + self.special_params = special_params + super().__init__() + def __call__(self, comb_class): + if comb_class.dimensions != (1, 1): + raise StrategyDoesNotApply + for param in self.special_params: + yield ParameterInsertionStrategy(ParameterList({param}))(comb_class) -class ContainerExorcismStrategy(AvoiderExorcismStrategy): - """Strategy for transforming Containers into Avoiders""" + @classmethod + def from_dict(cls, d: dict) -> "ParameterInsertionFactory": + return cls(**d) - cleaner = MTCleaner.make_full_cleaner("Container Exorcism Cleaner") + def __repr__(self) -> str: + return f"{self.__class__.__name__}()" - def decomposition_function(self, comb_class: MappedTiling): - contianer = tuple(self.params)[0] - avoiders, containers, enumerators = comb_class.ace_parameters() - assert self.params in containers, "Container Does Not Exist" - base = comb_class.tiling - new_avoiders = avoiders.add(contianer) - new_containers = list(containers) - new_containers.remove(self.params) - clean = self.__class__.cleaner - m1 = MappedTiling(base, avoiders, new_containers, enumerators) - m2 = MappedTiling(base, new_avoiders, new_containers, enumerators) - return (clean(m1), clean(m2)) + def __str__(self) -> str: + return "Special Param Insertion" class AvoiderExorcismFactory(StrategyFactory[MappedTiling]): """Transforms avoiders which map to a single cell into containers""" def __call__(self, comb_class): - for avoider in comb_class.avoiding_parameters: + avoiders, containers, enumerators = comb_class.ace_parameters() + for avoider in avoiders: if ( len(set(avoider.col_map.values())) == len(set(avoider.row_map.values())) == 1 ): - yield AvoiderExorcismStrategy(ParameterList({avoider})) + new_avoiders = ParameterList( + {param for param in avoiders if param != avoider} + ) + new_mappling = MappedTiling( + comb_class.tiling, new_avoiders, containers, enumerators + ) + yield ParameterInsertionStrategy(ParameterList({avoider}))(new_mappling) @classmethod def from_dict(cls, d: dict) -> "AvoiderExorcismFactory": @@ -366,7 +322,8 @@ class ContainerExorcismFactory(StrategyFactory[MappedTiling]): """Transforms solo containers which map to a single cell into avoiders""" def __call__(self, comb_class): - for c_list in comb_class.containing_parameters: + avoiders, containers, enumerators = comb_class.ace_parameters() + for c_list in containers: if len(c_list) > 1: continue for container in c_list: @@ -375,7 +332,25 @@ def __call__(self, comb_class): == len(container.row_map.values()) == 1 ): - yield ContainerExorcismStrategy(ParameterList({container})) + new_containers = ( + param_list for param_list in containers if param_list != c_list + ) + new_mappling = MappedTiling( + comb_class.tiling, avoiders, new_containers, enumerators + ) + yield ParameterInsertionStrategy(ParameterList({container}))( + new_mappling + ) + + @classmethod + def from_dict(cls, d: dict) -> "ContainerExorcismFactory": + return cls(**d) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}()" + + def __str__(self) -> str: + return "Container exorcism" class MapplingPointPlacementFactory(PointPlacementFactory): From ad7cea2fcee04b0f87ed88db8a9aa20a245a9446 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:11:46 +0000 Subject: [PATCH 35/38] Lots of Fixes Added checks to param insertion strategy. Fixed a bug in IL factoring. Fixed a bug in try to kill. New pack to get 123 tree. --- mapplings/algorithms/factor.py | 27 +++++++++----- mapplings/cleaners/mappling_cleaner.py | 11 +++++- mapplings/mapped_tiling.py | 4 +- mapplings/strategies/mapped_tilescope.py | 39 ++++++++++++++++++-- mapplings/strategies/tilescope_strategies.py | 7 ++-- playground/param_placement_play.py | 32 +++++++++++----- 6 files changed, 90 insertions(+), 30 deletions(-) diff --git a/mapplings/algorithms/factor.py b/mapplings/algorithms/factor.py index 4ff09b3..bec81f3 100644 --- a/mapplings/algorithms/factor.py +++ b/mapplings/algorithms/factor.py @@ -84,7 +84,7 @@ class ILFactorNormal(Factor): """Does IL factoring with 00 obs as normal""" @cached_property - def find_factors_as_cells(self): + def find_factors_as_cells(self) -> tuple[tuple[Cell, ...], ...]: self.combine_cells_in_obs_and_reqs() self.combine_cells_from_parameters() factors = [] @@ -99,16 +99,16 @@ def find_factors_as_cells(self): def make_enumerators(self): """Creates the enumerators needed for interleaving""" - factor_rows_and_cols = ( - map(tuple, map(set, zip(*factor))) for factor in self.find_factors_as_cells - ) - factor_rows_and_cols = tuple( - map(lambda x: tuple(chain.from_iterable(x)), zip(*factor_rows_and_cols)) - ) + cols_in_factors = list[int]() + rows_in_factors = list[int]() + for factor in self.find_factors_as_cells: + f_cols, f_rows = map(set, zip(*factor)) + cols_in_factors += list(f_cols) + rows_in_factors += list(f_rows) new_enumerators = set() dimensions = self.tiling.dimensions for row in range(dimensions[1]): - if factor_rows_and_cols[1].count(row) > 1: + if rows_in_factors.count(row) > 1: new_enumerators.add( ParameterList( ( @@ -123,7 +123,7 @@ def make_enumerators(self): ) ) for col in range(dimensions[0]): - if factor_rows_and_cols[0].count(col) > 1: + if cols_in_factors.count(col) > 1: new_enumerators.add( ParameterList( ( @@ -157,11 +157,18 @@ def combine_cells_in_obs_and_reqs(self): new_obs = set() for row in range(self.tiling.dimensions[1]): for col1, col2 in combinations(range(self.tiling.dimensions[0]), 2): - new_obs.add(GriddedCayleyPerm((0, 0), ((col1, row), (col2, row)))) + cell1, cell2 = (col1, row), (col2, row) + if cell1 in self.cells_dict and cell2 in self.cells_dict: + new_obs.add(GriddedCayleyPerm((0, 0), (cell1, cell2))) new_obs.symmetric_difference_update(set(self.tiling.obstructions)) for gcp in new_obs: if not self.point_row_ob(gcp): for cell, cell2 in combinations((gcp.find_active_cells()), 2): + + if cell2 not in self.cells_dict: + print(cell, cell2) + print(self.tiling) + print(gcp) self.combine_cells(cell, cell2) for cell, cell2 in chain.from_iterable( combinations( diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 3f6eb8b..62556c9 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -69,7 +69,10 @@ def _clean_parameters(mappling: MappedTiling) -> MappedTiling: for e_list in new_enumerators ] return MappedTiling( - mappling.tiling, new_avoiders, new_containers, new_enumerators + mappling.tiling, + new_avoiders, + new_containers, + new_enumerators, ) return _clean_parameters @@ -92,6 +95,7 @@ def try_to_kill(mappling: MappedTiling) -> MappedTiling: for avoider in mappling.avoiding_parameters ): return MappedTiling.empty_mappling() + return MappedTiling.empty_mappling() return MappedTiling( Tiling( [GriddedCayleyPerm(CayleyPermutation((0,)), ((0, 0),))], [], (1, 1) @@ -155,7 +159,10 @@ def reap_all_contradictions(mappling: MappedTiling) -> MappedTiling: if new_e_list: new_enumerators.append(e_list) return MappedTiling( - mappling.tiling, new_avoiders, new_containers, new_enumerators + mappling.tiling, + new_avoiders, + new_containers, + new_enumerators, ) @staticmethod diff --git a/mapplings/mapped_tiling.py b/mapplings/mapped_tiling.py index bd39794..4e7b461 100644 --- a/mapplings/mapped_tiling.py +++ b/mapplings/mapped_tiling.py @@ -129,7 +129,9 @@ def get_objects(self, n: int) -> Objects: objects[param].append(gcp) return objects - def get_parameters(self, obj: GriddedCayleyPerm) -> tuple[int, ...]: + def get_parameters( + self, obj: GriddedCayleyPerm + ) -> tuple[int, ...]: # This function is broken """Parameters are not what you think!!! This is specific to combinatorical class parameters""" return tuple( diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 77ab648..559c464 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -1,7 +1,7 @@ """Different strategy packs for MappedTileScope.""" from comb_spec_searcher import StrategyPack, AtomStrategy -from mapplings import MappedTiling +from mapplings import MappedTiling, ParameterList, Parameter from .verification_strategy import ( NoParameterVerificationStrategy, MapplingVerticalInsertionEncodableVerificationStrategy, @@ -21,7 +21,10 @@ MapplingLessThanOrEqualRowColSeparationStrategy, MapplingCellInsertionFactory, ParamPlacementFactory, - AvoiderExorcismFactory, + # AvoiderExorcismFactory, + ParameterInsertionFactory, + MapplingILFactorStrategy, + MapplingInvertedILFactorStrategy, ) @@ -326,8 +329,7 @@ def parameter_tomfoolery(cls, rootmt: MappedTiling): inferral_strats=[], expansion_strats=[ [ - ParamPlacementFactory(), - AvoiderExorcismFactory(), + MapplingLessThanRowColSeparationStrategy(), ] ], ver_strats=[ @@ -340,3 +342,32 @@ def parameter_tomfoolery(cls, rootmt: MappedTiling): symmetries=[], iterative=False, ) + + @classmethod + def pack_for_123(cls, special_param: Parameter): + return MappedTileScopePack( + initial_strats=[ + MapplingFactorStrategy(), + MapplingInvertedILFactorStrategy(), + MapplingILFactorStrategy(), + ], + inferral_strats=[ + MapplingCellInsertionFactory(), + ParamPlacementFactory(), + ], + expansion_strats=[ + [ + ParameterInsertionFactory(ParameterList({special_param})), + MapplingPointPlacementFactory(), + ] + ], + ver_strats=[ + AtomStrategy(), + # NoParameterVerificationStrategy(rootmt), + MapplingVerticalInsertionEncodableVerificationStrategy(), + MapplingHorizontalInsertionEncodableVerificationStrategy(), + ], + name="Param Nonsense", + symmetries=[], + iterative=False, + ) diff --git a/mapplings/strategies/tilescope_strategies.py b/mapplings/strategies/tilescope_strategies.py index 3015fe3..5b27fcb 100644 --- a/mapplings/strategies/tilescope_strategies.py +++ b/mapplings/strategies/tilescope_strategies.py @@ -272,10 +272,9 @@ def __init__(self, special_params: Iterable[Parameter]): super().__init__() def __call__(self, comb_class): - if comb_class.dimensions != (1, 1): - raise StrategyDoesNotApply - for param in self.special_params: - yield ParameterInsertionStrategy(ParameterList({param}))(comb_class) + if comb_class.dimensions == (1, 1) and comb_class.active_cells: + for param in self.special_params: + yield ParameterInsertionStrategy(ParameterList({param}))(comb_class) @classmethod def from_dict(cls, d: dict) -> "ParameterInsertionFactory": diff --git a/playground/param_placement_play.py b/playground/param_placement_play.py index cb3c448..5c9eed5 100644 --- a/playground/param_placement_play.py +++ b/playground/param_placement_play.py @@ -8,25 +8,39 @@ from comb_spec_searcher import CombinatorialSpecificationSearcher from comb_spec_searcher.rule_db import RuleDBForest -ghost = Tiling.from_vincular_with_obs(CayleyPermutation((0,)), []) +MTCleaner.global_debug_toggle(0) +ghost = Tiling.from_vincular_with_obs(CayleyPermutation((0, 1)), []) ghost = ghost.add_obstructions( ( + GriddedCayleyPerm((0,), [(0, 0)]), GriddedCayleyPerm((0,), [(0, 2)]), GriddedCayleyPerm((0,), [(2, 0)]), - GriddedCayleyPerm((0,), [(0, 1)]), - GriddedCayleyPerm((0,), [(2, 1)]), + GriddedCayleyPerm((0,), [(2, 2)]), + GriddedCayleyPerm((0,), [(4, 4)]), ) ) -P = Parameter(ghost, RowColMap({0: 0, 1: 0, 2: 0}, {0: 0, 1: 0, 2: 0})) -B = Tiling([GriddedCayleyPerm((0, 0), [(0, 0), (0, 0)])], [], (1, 1)) +P = Parameter( + ghost, RowColMap({0: 0, 1: 0, 2: 0, 3: 0, 4: 0}, {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}) +) +B = Tiling( + [ + GriddedCayleyPerm((0, 0), [(0, 0), (0, 0)]), + GriddedCayleyPerm((0, 1, 2), [(0, 0), (0, 0), (0, 0)]), + ], + [], + (1, 1), +) +print(P) ruledb = RuleDBForest() c_list = ParameterList([P]) -mappling = MappedTiling(B, [P], [], []) +mappling = MappedTiling(B, [], [], []) mappling = MTCleaner.full_cleanup(mappling) -pack = MappedTileScopePack.parameter_tomfoolery(None) -searcher = CombinatorialSpecificationSearcher(mappling, pack, debug=True, ruledb=ruledb) +pack = MappedTileScopePack.pack_for_123(P) +searcher = CombinatorialSpecificationSearcher( + mappling, pack, debug=False, ruledb=ruledb +) spec = searcher.auto_search( - status_update=10, + status_update=30, ) spec.show() From af70889fb436f883740f72671a04187bfdbd8b5b Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:43:58 +0000 Subject: [PATCH 36/38] tox fix --- mapplings/cleaners/mappling_cleaner.py | 1 + mapplings/strategies/mapped_tilescope.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index 62556c9..c443f50 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -95,6 +95,7 @@ def try_to_kill(mappling: MappedTiling) -> MappedTiling: for avoider in mappling.avoiding_parameters ): return MappedTiling.empty_mappling() + ): return MappedTiling.empty_mappling() return MappedTiling( Tiling( diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 559c464..28c166a 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -344,20 +344,23 @@ def parameter_tomfoolery(cls, rootmt: MappedTiling): ) @classmethod - def pack_for_123(cls, special_param: Parameter): + def pack_for_1_32( + cls, + ): + """Pack to get the 1-32 tree""" return MappedTileScopePack( initial_strats=[ - MapplingFactorStrategy(), + # MapplingFactorStrategy(), MapplingInvertedILFactorStrategy(), MapplingILFactorStrategy(), ], inferral_strats=[ MapplingCellInsertionFactory(), - ParamPlacementFactory(), + # ParamPlacementFactory(), ], expansion_strats=[ [ - ParameterInsertionFactory(ParameterList({special_param})), + # ParameterInsertionFactory(ParameterList({special_param})), MapplingPointPlacementFactory(), ] ], @@ -367,7 +370,7 @@ def pack_for_123(cls, special_param: Parameter): MapplingVerticalInsertionEncodableVerificationStrategy(), MapplingHorizontalInsertionEncodableVerificationStrategy(), ], - name="Param Nonsense", + name="1-32", symmetries=[], iterative=False, ) From 41a21c3e49d41d6a83bc2ddaad5295a5d94bea36 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:44:19 +0000 Subject: [PATCH 37/38] tox --- mapplings/cleaners/mappling_cleaner.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mapplings/cleaners/mappling_cleaner.py b/mapplings/cleaners/mappling_cleaner.py index c443f50..da25874 100644 --- a/mapplings/cleaners/mappling_cleaner.py +++ b/mapplings/cleaners/mappling_cleaner.py @@ -95,6 +95,11 @@ def try_to_kill(mappling: MappedTiling) -> MappedTiling: for avoider in mappling.avoiding_parameters ): return MappedTiling.empty_mappling() + if any( + ( + all(container.positive_cells() for container in c_list) + for c_list in mappling.containing_parameters + ) ): return MappedTiling.empty_mappling() return MappedTiling( From 59aa8915e8d0cb4405bd80f89e2f21b6d607f791 Mon Sep 17 00:00:00 2001 From: ReedActon <110858904+ReedActon@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:49:08 +0000 Subject: [PATCH 38/38] Pylint --- mapplings/strategies/mapped_tilescope.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapplings/strategies/mapped_tilescope.py b/mapplings/strategies/mapped_tilescope.py index 28c166a..cb689ad 100644 --- a/mapplings/strategies/mapped_tilescope.py +++ b/mapplings/strategies/mapped_tilescope.py @@ -1,7 +1,7 @@ """Different strategy packs for MappedTileScope.""" from comb_spec_searcher import StrategyPack, AtomStrategy -from mapplings import MappedTiling, ParameterList, Parameter +from mapplings import MappedTiling from .verification_strategy import ( NoParameterVerificationStrategy, MapplingVerticalInsertionEncodableVerificationStrategy, @@ -20,9 +20,9 @@ MapplingLessThanRowColSeparationStrategy, MapplingLessThanOrEqualRowColSeparationStrategy, MapplingCellInsertionFactory, - ParamPlacementFactory, + # ParamPlacementFactory, # AvoiderExorcismFactory, - ParameterInsertionFactory, + # ParameterInsertionFactory, MapplingILFactorStrategy, MapplingInvertedILFactorStrategy, )