Skip to content

Unfusion #496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: predicates
Choose a base branch
from
Open
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
26 changes: 24 additions & 2 deletions tilings/algorithms/fusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ def predicate_fusable(self) -> bool:
elif isinstance(ass, OppositeParityAssumption):
opposite.add(next(iter(ass.cells)))
left = self.left_fuse_region()
if len(left) > 1:
# TODO: can't fuse things like
# O | O
# - + -
# O | O
# need entire rows/columns to be tracked as odd.
return False
for (x, y) in left:
xn, yn = x, y
if self._fuse_row:
Expand All @@ -436,7 +443,7 @@ def predicate_fusable(self) -> bool:
return False
return True

def fused_tiling(self) -> "Tiling":
def fused_tiling(self, add_odd_req: bool = True) -> "Tiling":
"""
Return the fused tiling.
"""
Expand All @@ -450,10 +457,14 @@ def fused_tiling(self) -> "Tiling":
assumptions.extend(self.fused_predicates())
if self._tracked:
assumptions.append(self.new_assumption())
requirements = list(list(fc) for fc in self.requirements_fuse_counters)
requirements: List[Iterable[GriddedPerm]] = list(
list(fc) for fc in self.requirements_fuse_counters
)
if self._positive_left or self._positive_right:
new_positive_requirement = self.new_positive_requirement()
requirements = requirements + [new_positive_requirement]
if add_odd_req and self.is_odd_next_to_odd_fusion():
requirements += [map(GriddedPerm.point_perm, self.left_fuse_region())]
self._fused_tiling = self._tiling.__class__(
obstructions=self.obstruction_fuse_counter.keys(),
requirements=requirements,
Expand All @@ -463,6 +474,17 @@ def fused_tiling(self) -> "Tiling":
)
return self._fused_tiling

def clear_fused_tiling(self) -> None:
self._fused_tiling = None

def is_odd_next_to_odd_fusion(self) -> bool:
return (
OddCountAssumption.from_cells(self.left_fuse_region())
in self._tiling.assumptions
and OddCountAssumption.from_cells(self.right_fuse_region())
in self._tiling.assumptions
)

def fuse_assumption(self, assumption: AssumptionClass) -> AssumptionClass:
return assumption.__class__(self.fuse_gridded_perm(gp) for gp in assumption.gps)

Expand Down
2 changes: 1 addition & 1 deletion tilings/algorithms/minimal_gridded_perms.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ def odd_even_minimal_gridded_perms(
self, odd_cells: Set[Cell], even_cells: Set[Cell]
) -> Set[GriddedPerm]:
if odd_cells.intersection(even_cells):
return
return set()

def cell_count(gp: GriddedPerm, cell: Cell) -> int:
return sum(1 for _ in filter(cell.__eq__, gp.pos))
Expand Down
32 changes: 28 additions & 4 deletions tilings/algorithms/obstruction_inferral.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import abc
from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple

from permuta import Perm
from tilings import GriddedPerm
from tilings.algorithms.gridded_perm_generation import GriddedPermsOnTiling
from tilings.assumptions import OddCountAssumption

if TYPE_CHECKING:
from tilings import Tiling
Expand Down Expand Up @@ -39,13 +41,35 @@ def new_obs(self, yield_non_minimal: bool = False) -> List[GriddedPerm]:
self._new_obs = []
return self._new_obs

extra_reqs = []
assumptions = list(self._tiling.assumptions)
for ass in assumptions:
if isinstance(ass, OddCountAssumption) and len(ass.cells) == 1:
to_add = [GriddedPerm.single_cell(Perm((0,)), next(iter(ass.cells)))]
if to_add not in self._tiling.requirements:
extra_reqs.append(to_add)

fake_tiling = self._tiling.__class__(
self._tiling.obstructions,
self._tiling.requirements + tuple(extra_reqs),
self._tiling.assumptions,
remove_empty_rows_and_cols=False,
derive_empty=False,
simplify=False,
sorted_input=False,
already_minimized_obs=True,
checked=True,
)

max_len_of_perms_to_check = max(map(len, perms_to_check))
max_length = (
self._tiling.maximum_length_of_minimum_gridded_perm()
fake_tiling.maximum_length_of_minimum_gridded_perm()
+ max_len_of_perms_to_check
)

GP = GriddedPermsOnTiling(
self._tiling, yield_non_minimal=yield_non_minimal
fake_tiling,
yield_non_minimal=yield_non_minimal,
).gridded_perms(max_length, place_at_most=max_len_of_perms_to_check)
perms_left = set(perms_to_check)
for gp in GP:
Expand Down Expand Up @@ -122,8 +146,8 @@ def potential_new_obs(self) -> List[GriddedPerm]:
"""
Iterator over all possible obstruction of `self.obstruction_length`.
"""
if not self._tiling.requirements:
return []
# if not self._tiling.requirements:
# return []
no_req_tiling = self._tiling.__class__(self._tiling.obstructions)
n = self._obs_len
pot_obs = filter(self.not_required, no_req_tiling.gridded_perms(n))
Expand Down
8 changes: 6 additions & 2 deletions tilings/strategies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
from .row_and_col_separation import RowColumnSeparationStrategy
from .sliding import SlidingFactory
from .symmetry import SymmetriesFactory
from .unfusion import UnfusionColumnStrategy, UnfusionFactory, UnfusionRowStrategy
from .unfusion import (
UnfusionColumnStrategy,
UnfusionRowColumnFactory,
UnfusionRowStrategy,
)
from .verification import (
BasicVerificationStrategy,
ComponentVerificationStrategy,
Expand Down Expand Up @@ -93,7 +97,7 @@
"RequirementPointingFactory",
"UnfusionColumnStrategy",
"UnfusionRowStrategy",
"UnfusionFactory",
"UnfusionRowColumnFactory",
# Equivalence
"MonotoneSlidingFactory",
"PatternPlacementFactory",
Expand Down
3 changes: 1 addition & 2 deletions tilings/strategies/fusion/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from tilings import GriddedPerm, Tiling
from tilings.algorithms import ComponentFusion

from .constructor import FusionConstructor
from .fusion import FusionStrategy


Expand Down Expand Up @@ -39,7 +38,7 @@ def is_positive_or_empty_fusion(self, tiling: Tiling) -> bool:

def constructor(
self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None
) -> FusionConstructor:
) -> Constructor:
if self.tracked and self.is_positive_or_empty_fusion(comb_class):
raise NotImplementedError(
"Can't count positive or empty fusion. Try a cell insertion!"
Expand Down
65 changes: 60 additions & 5 deletions tilings/strategies/fusion/fusion.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections import defaultdict
from itertools import chain, islice
from itertools import chain, islice, product
from random import randint
from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, cast

Expand All @@ -9,7 +9,8 @@
from comb_spec_searcher.typing import Objects
from tilings import GriddedPerm, Tiling
from tilings.algorithms import Fusion
from tilings.assumptions import OddCountAssumption
from tilings.assumptions import EvenCountAssumption, OddCountAssumption
from tilings.strategies.requirement_insertion import RequirementInsertionStrategy

from ..pointing import DivideByK
from .constructor import FusionConstructor, ReverseFusionConstructor
Expand Down Expand Up @@ -195,7 +196,7 @@ def shifts(

def constructor(
self, comb_class: Tiling, children: Optional[Tuple[Tiling, ...]] = None
) -> FusionConstructor:
) -> Constructor:
if not self.tracked:
# constructor only enumerates when tracked.
raise NotImplementedError("The fusion strategy was not tracked.")
Expand All @@ -207,6 +208,15 @@ def constructor(
assert children is None or children == (child,)
min_left, min_right = algo.min_left_right_points()
left, right = algo.left_fuse_region(), algo.right_fuse_region()
odd_left, odd_right = None, None
if OddCountAssumption.from_cells(left) in comb_class.assumptions:
odd_left = True
elif EvenCountAssumption.from_cells(left) in comb_class.assumptions:
odd_left = False
if OddCountAssumption.from_cells(right) in comb_class.assumptions:
odd_right = True
if EvenCountAssumption.from_cells(right) in comb_class.assumptions:
odd_right = False
return FusionConstructor(
comb_class,
child,
Expand All @@ -215,8 +225,8 @@ def constructor(
*self.left_right_both_sided_parameters(comb_class),
min_left,
min_right,
odd_left=(OddCountAssumption.from_cells(left) in comb_class.assumptions),
odd_right=(OddCountAssumption.from_cells(right) in comb_class.assumptions),
odd_left=odd_left,
odd_right=odd_right,
)

def reverse_constructor( # pylint: disable=no-self-use
Expand Down Expand Up @@ -421,6 +431,10 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]:
yield FusionStrategy(row_idx=row_idx, tracked=self.tracked)(
comb_class, (fused_tiling,)
)
# yield from self._all_unfusion_rules(True, row_idx, fused_tiling)
if algo.is_odd_next_to_odd_fusion():
yield self._extra_cell_insertion_rule(algo)

for col_idx in range(cols - 1):
algo = Fusion(
comb_class,
Expand All @@ -433,6 +447,47 @@ def __call__(self, comb_class: Tiling) -> Iterator[Rule]:
yield FusionStrategy(col_idx=col_idx, tracked=self.tracked)(
comb_class, (fused_tiling,)
)
# yield from self._all_unfusion_rules(False, col_idx, fused_tiling)
if algo.is_odd_next_to_odd_fusion():
yield self._extra_cell_insertion_rule(algo)

def _all_unfusion_rules(
self, row: bool, idx: int, tiling: Tiling
) -> Iterator[Rule]:
# pylint: disable=import-outside-toplevel
from tilings.strategies.fusion.unfusion import UnfusionStrategy

for left, right, both in product((True, False), (True, False), (True, False)):
if left or right or both:
if row:
yield UnfusionStrategy(
row_idx=idx,
tracked=self.tracked,
left=left,
right=right,
both=both,
)(tiling)
else:
yield UnfusionStrategy(
col_idx=idx,
tracked=self.tracked,
left=left,
right=right,
both=both,
)(tiling)

@staticmethod
def _extra_cell_insertion_rule(algo: Fusion) -> Rule:
algo.clear_fused_tiling()
fused = algo.fused_tiling(add_odd_req=False)
algo.clear_fused_tiling()
cells = [
fused.forward_map.map_cell(cell)
for cell in filter(
fused.forward_map.is_mappable_cell, algo.left_fuse_region()
)
]
return RequirementInsertionStrategy(map(GriddedPerm.point_perm, cells))(fused)

def __str__(self) -> str:
if self.tracked:
Expand Down
Loading