Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions mapplings/algorithms/param_redundancy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from typing import Iterable
from itertools import chain, combinations, product
from gridded_cayley_permutations import Tiling, RowColMap
from gridded_cayley_permutations.simplify_obstructions_and_requirements import (
SimplifyObstructionsAndRequirements as Simplify,
)

from mapplings import ParameterList, Parameter


class CompareParameters:
"""Used to check for redundant parameters"""

def __init__(self, params: Iterable[Parameter]):
self.params = ParameterList(params)
self.redundant = set[Parameter]()
self.find_redundant()

def __call__(self) -> ParameterList:
return ParameterList(
param for param in self.params if param not in self.redundant
)

@staticmethod
def all_maps(
param1: Parameter, param2: Parameter
) -> tuple[tuple[RowColMap, ...], tuple[RowColMap, ...]]:
"""Every way to map from one param to the other"""
preimages1, preimages2 = param1.map.preimage_map(), param2.map.preimage_map()
if param1.positive_cells():
positive1 = tuple(map(set[int], zip(*param1.positive_cells())))
else:
positive1 = (set[int](), set[int]())
if param2.positive_cells():
positive2 = tuple(map(set[int], zip(*param2.positive_cells())))
else:
positive2 = (set[int](), set[int]())

def make_maps(
row_maps: bool,
) -> tuple[tuple[dict[int, int], ...], tuple[dict[int, int], ...]]:
all_sections1, all_sections2 = (
list[set[tuple[tuple[int, int], ...]]](),
list[set[tuple[tuple[int, int], ...]]](),
)
for image in set(preimages1[row_maps].keys()) & set(
preimages2[row_maps].keys()
):
check_positive = (
set(preimages1[row_maps][image]) & positive1[row_maps],
set(preimages2[row_maps][image]) & positive2[row_maps],
)
section_maps1, section_maps2 = (
set[tuple[tuple[int, int], ...]](),
set[tuple[tuple[int, int], ...]](),
)
if len(preimages1[row_maps][image]) > len(preimages2[row_maps][image]):
for indices in combinations(
preimages1[row_maps][image], len(preimages2[row_maps][image])
):

if not check_positive[0].issubset(indices):
continue
section_maps1.add(
tuple(zip(indices, preimages2[row_maps][image]))
)
section_maps2.add(
tuple(zip(preimages2[row_maps][image], indices))
)
else:
for indices in combinations(
preimages2[row_maps][image], len(preimages1[row_maps][image])
):
if not check_positive[1].issubset(indices):
continue
section_maps2.add(
tuple(zip(indices, preimages1[row_maps][image]))
)
section_maps1.add(
tuple(zip(preimages1[row_maps][image], indices))
)
all_sections1.append(section_maps1)
all_sections2.append(section_maps2)
return tuple(
dict(chain(*tuples)) for tuples in product(*all_sections1)
), tuple(dict(chain(*tuples)) for tuples in product(*all_sections2))

col_maps = make_maps(False)
row_maps = make_maps(True)
return tuple(
RowColMap(*maps) for maps in product(col_maps[0], row_maps[0])
), tuple(RowColMap(*maps) for maps in product(col_maps[1], row_maps[1]))

@staticmethod
def redundancy_check(param1: Parameter, param2: Parameter):
"""Returns true if param1 implies param2"""
positive_images1 = {
(param1.col_map[x], param1.row_map[y]) for x, y in param1.positive_cells()
}
positive_images2 = {
(param2.col_map[x], param2.row_map[y]) for x, y in param2.positive_cells()
}
intersection = param1.image_cells() & param2.image_cells()
if not (
positive_images1.issubset(intersection)
and positive_images2.issubset(intersection)
):
return False
maps = CompareParameters.all_maps(param1, param2)[0]
req_free2 = Tiling(param2.obstructions, [], param2.dimensions)

def _check(temp_map: RowColMap) -> bool:
temp_param = Parameter(param1.ghost, temp_map)
if any(
req_free2.gcp_in_tiling(temp_param.map.map_gridded_cperm(ob))
for ob in temp_param.obstructions
):
print("NO", temp_param.map)
return False
for req_list in temp_param.requirements:
if not Simplify.requirement_implied_by_some_requirement(
temp_param.map.map_gridded_cperms(req_list), param2.requirements
):
print("NO", temp_param.map)
return False
print("Yes?", temp_param.map)
return True

for temp_map in maps:
if _check(temp_map):
return True
return False

def find_redundant(self) -> None:
"""Finds all redundant parameters in the list"""
for p1, p2 in combinations(self.params, 2):
if {p1, p2} & self.redundant:
continue
if self.redundancy_check(p1, p2):
self.redundant.add(p1)
continue
if self.redundancy_check(p2, p1):
self.redundant.add(p2)
4 changes: 1 addition & 3 deletions mapplings/cleaners/cleaner.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,7 @@ def status_update(cls) -> str:
"right",
"right",
)
logs = list(
log for log in cls.all_loggers if log.log_level > 0 and log.runs > 0
)
logs = list(log for log in cls.all_loggers if log.log_level > 0)
if not logs:
return f"Logging dissabled for all {cls.__name__} cleaners.\n"
logs.sort(key=lambda log: log.total_times, reverse=True)
Expand Down
6 changes: 3 additions & 3 deletions mapplings/cleaners/mappling_cleaner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .cleaner import GenericCleaner, Register, CleanerLog
from .parameter_cleaner import ParamCleaner


default_param_cleaner = ParamCleaner.make_full_cleaner("Param Default Cleaner")


Expand Down Expand Up @@ -226,10 +227,9 @@ def remove_empty_rows_and_cols(mappling: MappedTiling) -> MappedTiling:
@reg(12)
def simple_reduce_redundant_parameters(mappling: MappedTiling) -> MappedTiling:
"""Removes any parameter implied by another with a basic check"""
new_avoiders = mappling.avoiding_parameters.simple_remove_redundant(True)
new_avoiders = mappling.avoiding_parameters.remove_redundant()
new_containers = [
c_list.simple_remove_redundant()
for c_list in mappling.containing_parameters
c_list.remove_redundant() for c_list in mappling.containing_parameters
]
return MappedTiling(
mappling.tiling,
Expand Down
55 changes: 51 additions & 4 deletions mapplings/cleaners/parameter_cleaner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Iterator, Iterable
from itertools import chain
from cayley_permutations import CayleyPermutation
from gridded_cayley_permutations.row_col_map import RowColMap
from gridded_cayley_permutations.unplacement import PartialUnplacement
from gridded_cayley_permutations import Tiling
Expand All @@ -22,15 +23,15 @@ class ParamCleaner(GenericCleaner[Parameter]):
run_on_enumerators=True,
)
global_tracker = CleanerLog[Parameter](
reg.registered_functions, name="Global Tracker"
reg.registered_functions, log_level=2,name="Global Tracker"
)

all_loggers = {global_tracker}

# Final Methods

@staticmethod
@reg(3, run_on_enumerators=False)
@reg(5, run_on_enumerators=False)
def reduce_by_fusion(param: Parameter) -> Parameter:
"""Fuses valid rows and columns"""
deleted_cols, deleted_rows = set(
Expand Down Expand Up @@ -76,7 +77,7 @@ def reduce_empty_rows_and_cols(param: Parameter) -> Parameter:
def remove_blank_rows_and_cols(param: Parameter) -> Parameter:
"""Deletes all rows and cols which have no obs or reqs"""

blank = tuple(map(set[int], param.blank_and_near_blank()))
blank = tuple(map(set[int], param.find_blank_columns_and_rows()))
if not any(blank):
return param
col_preimages, row_preimages = param.map.preimage_map()
Expand Down Expand Up @@ -116,7 +117,7 @@ def to_remove(
return param.delete_rows_and_columns(cols_to_remove, rows_to_remove)

@staticmethod
@reg(2, run_on_enumerators=False)
@reg(4, run_on_enumerators=False)
def unplace_points(param: Parameter) -> Parameter:
"""Unplaces all possible points in the parameter"""
algo = PartialUnplacement(param.ghost)
Expand Down Expand Up @@ -147,6 +148,52 @@ def unplace_points(param: Parameter) -> Parameter:
}
return Parameter(new_ghost, RowColMap(new_col_map, new_row_map))

@staticmethod
@reg(2, run_on_enumerators=False)
def insert_blank(param: Parameter) -> Parameter:
"""Inserts a blank col/row in between descents/ascents wherever possible"""
if not param.requirements:
return param
to_insert = [set[int](), set[int]()]
maps = param.col_map, param.row_map
blank = tuple(map(set[int], param.find_blank_columns_and_rows()))
point_indices = param.point_cols, param.point_rows

def validate(index1: int, index2: int, check_rows: bool) -> bool:
indeices = {index1, index2}
if maps[check_rows][index1] != maps[check_rows][index2]:
return False
if not indeices.issubset(point_indices[check_rows]):
return False
if {index1 - 1, index2 + 1} & blank[check_rows]:
return True
return False

for req_list in param.requirements:
if not len(req_list) == 1:
continue
req = req_list[0]
if req.pattern not in (
CayleyPermutation((0, 1)),
CayleyPermutation((1, 0)),
):
continue
cell1, cell2 = req.positions
if (cell1[0] != cell2[0]) and (cell1[1] != cell2[1]):
continue
if cell1[0] + 1 == cell2[0]:
if validate(cell1[0], cell2[0], False):
to_insert[0].add(cell1[0])
continue
idx1, idx2 = sorted([cell1[1], cell2[1]])
if idx1 + 1 == idx2:
if validate(idx1, idx2, True):
to_insert[1].add(idx1)
if not any(to_insert):
return param

return param.insert_cols_and_rows(*to_insert)

# Internal Methods

@staticmethod
Expand Down
Loading
Loading