Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a248295
Update README.md
Ollson2921 Nov 7, 2025
8896d5f
Rename README.md to README.rst
Ollson2921 Nov 7, 2025
994a71e
added strategy
Ollson2921 Mar 16, 2026
ebda413
added to strategy packs
Ollson2921 Mar 16, 2026
ccfe278
make mappling empty if any empty avoiding params
Ollson2921 Mar 18, 2026
b529c4f
Merge pull request #97 from Ollson2921/main
Ollson2921 Apr 2, 2026
0dfc6a4
Merge pull request #100 from Ollson2921/main
Ollson2921 May 4, 2026
5187144
Merge branch 'obstruction-transitivity' into obs-trans-updates
Ollson2921 May 4, 2026
e183543
Merge pull request #102 from Ollson2921/obs-trans-updates
Ollson2921 May 4, 2026
b436dc5
missing brackets
Ollson2921 May 4, 2026
a580ec8
typo
Ollson2921 May 4, 2026
d5748e7
add_obstructions to params create params, not tilings
Ollson2921 May 4, 2026
4b4fd1f
tox
Ollson2921 May 4, 2026
295f4e9
tox
Ollson2921 May 4, 2026
7466aca
toml rather than setup.py
Ollson2921 May 4, 2026
0af6067
wrong branch
Ollson2921 May 4, 2026
2516812
hatchling
Ollson2921 May 4, 2026
3f9050c
update black
Ollson2921 May 4, 2026
6f515a8
black
Ollson2921 May 4, 2026
d8ba447
css on develop
Ollson2921 May 4, 2026
fab0468
undo last commit
Ollson2921 May 4, 2026
b11ac0b
Unplacement Class
ReedActon May 7, 2026
4cf08fb
Get rid of prints
ReedActon May 7, 2026
76b119f
Merge pull request #111 from Ollson2921/main
Ollson2921 May 11, 2026
744c688
only make mappling empty if empty gcp not in obs
Ollson2921 May 12, 2026
d6a07c9
dont code online
Ollson2921 May 12, 2026
8121808
getting there
Ollson2921 May 12, 2026
4d842e5
tox
Ollson2921 May 12, 2026
744746f
tox
Ollson2921 May 12, 2026
5938db3
pylint
Ollson2921 May 12, 2026
2cf542c
mypy
Ollson2921 May 12, 2026
8d3f422
mypy
Ollson2921 May 12, 2026
ff02566
enumerators are param lists
Ollson2921 May 12, 2026
2f1e999
Added Base TIling
ReedActon May 12, 2026
f12a552
Various FIxes
ReedActon May 13, 2026
f7f8f00
Cleaning
ReedActon May 13, 2026
2d1f0e4
Merge pull request #95 from Ollson2921/empty-mapplings
Ollson2921 May 13, 2026
982b326
Merge pull request #103 from Ollson2921/obstruction-transitivity
Ollson2921 May 13, 2026
d8c6f14
Merge pull request #98 from Ollson2921/readme
Ollson2921 May 13, 2026
05f10c6
Merge branch 'main' into unplacement_fix
ReedActon May 13, 2026
4daa198
mypy
ReedActon May 13, 2026
648c92a
Merge pull request #112 from Ollson2921/unplacement_fix
Ollson2921 May 13, 2026
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
1 change: 0 additions & 1 deletion README.md

This file was deleted.

29 changes: 29 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###############################
MapplePy
###############################

MapplePy is a python library for enumerating Cayley permutation classes which avoid non-classical patterns and grid classes.

A Cayley permutation is a word `π ∈ ℕ*` such that every number between 1 and the maximum value of `π` appears at least once. Cayley permutations can be seen as a generalisation of permutations where repeated values are allowed. Definitions of pattern containment and Cayley permutation classes follow the same ideas as defined for permutations where the patterns contained are also Cayley permutations, so the Cayley permutation class Av(11) describes all permutations. Cayley permutations are in bijection with ordered set partitions.

If you need support, you can join us in our `Discord support server`_.

.. _Discord support server: https://discord.gg/ngPZVT5

==========
Installing
==========

To install MapplePy on your system, run the following after cloning the repository:

.. code-block:: bash

./pip install .

It is also possible to install MapplePy in development mode to work on the
source code, in which case you run the following after cloning the repository:

.. code-block:: bash

./pip install -e .

1 change: 0 additions & 1 deletion mapplings/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from .row_col_sep_mt import MTLTRowColSeparation, MTLTORERowColSeparation
from .point_placement import MTRequirementPlacement


__all__ = [
"MTFactors",
"MTILFactorNormal",
Expand Down
1 change: 0 additions & 1 deletion mapplings/algorithms/factor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from mapplings import MappedTiling, ParameterList, Parameter
from mapplings.cleaners import MTCleaner


Cell = tuple[int, int]


Expand Down
51 changes: 37 additions & 14 deletions mapplings/cleaners/mappling_cleaner.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from .cleaner import GenericCleaner, Register, CleanerLog
from .parameter_cleaner import ParamCleaner
from .unplacement import ParamUnplacement

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

Expand Down Expand Up @@ -83,6 +84,22 @@ def fully_clean_parameters(mappling: MappedTiling) -> MappedTiling:
"""Applies all parameter cleanning functions to all parameters"""
return MTCleaner.clean_parameters(default_param_cleaner)(mappling)

@staticmethod
@reg(7)
def param_unplacement(mappling: MappedTiling) -> MappedTiling:
"""Applies point unplacement to all avoiders and containers"""
avoiders, containers, enumerators = mappling.ace_parameters()
new_avoiders = ParameterList(
ParamUnplacement(av, mappling).auto_unplace() for av in avoiders
)
new_containers = [
ParameterList(
ParamUnplacement(co, mappling).auto_unplace() for co in c_list
)
for c_list in containers
]
return MappedTiling(mappling.tiling, new_avoiders, new_containers, enumerators)

@staticmethod
@reg(0)
def try_to_kill(mappling: MappedTiling) -> MappedTiling:
Expand All @@ -106,7 +123,7 @@ def try_to_kill(mappling: MappedTiling) -> MappedTiling:
return mappling

@staticmethod
@reg(8, False) # Broken
@reg(9, False) # Broken
def factor_containters(mappling: MappedTiling) -> MappedTiling:
"""Factors out the intersection factors of a containing parameter list"""
new_containers = list(
Expand All @@ -129,7 +146,7 @@ def factor_containters(mappling: MappedTiling) -> MappedTiling:
)

@staticmethod
@reg(10, update_register=False)
@reg(11, update_register=False)
def backmap_points(mappling: MappedTiling) -> MappedTiling:
"""Backmaps point obstructions to all parameters"""
point_obstructions = (ob for ob in mappling.obstructions if len(ob) == 1)
Expand All @@ -152,14 +169,22 @@ def reap_all_contradictions(mappling: MappedTiling) -> MappedTiling:
return MappedTiling.empty_mappling()
new_containers.append(new_c_list)
new_avoiders = mappling.avoiding_parameters.remove_contradictions(base)
new_enumerators = []
avoiders: list[Parameter] = []
for param in new_avoiders:
if param.dimensions == (0, 0):
if (
not GriddedCayleyPerm(CayleyPermutation(()), ())
in param.obstructions
):
return MappedTiling.empty_mappling()
else:
avoiders.append(param)
new_enumerators: list[ParameterList] = []
for e_list in mappling.enumerating_parameters:
new_e_list = e_list.remove_contradictions(base)
if new_e_list:
new_enumerators.append(e_list)
return MappedTiling(
mappling.tiling, new_avoiders, new_containers, new_enumerators
)
return MappedTiling(mappling.tiling, avoiders, new_containers, new_enumerators)

@staticmethod
@reg(2)
Expand Down Expand Up @@ -224,7 +249,7 @@ def remove_empty_rows_and_cols(mappling: MappedTiling) -> MappedTiling:
)

@staticmethod
@reg(12)
@reg(13)
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()
Expand All @@ -247,11 +272,11 @@ def reduce_all_parameter_gcps(mappling: MappedTiling) -> MappedTiling:
avoiders, containers, enumerators = mappling.apply_to_all_parameters(
param_reducer
).ace_parameters()
new_avoiders = ParameterList(av for av in avoiders if av.dimensions != (0, 0))
new_avoiders = ParameterList(avoiders)
return MappedTiling(mappling.tiling, new_avoiders, containers, enumerators)

@staticmethod
@reg(11)
@reg(12)
def small_ob_inferral(mappling: MappedTiling) -> MappedTiling:
"""Adds point obstructions implied by param point cells
and small base tiling obstructions"""
Expand All @@ -270,7 +295,6 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling:
for avoider in new_mappling.avoiding_parameters.apply_to_all(
MTCleaner._cayley_ob_adjust_param, (ob,)
)
if avoider.dimensions != (0, 0)
)
new_containers = tuple(
ParameterList(
Expand All @@ -293,7 +317,6 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling:
for avoider in new_mappling.avoiding_parameters.apply_to_all(
MTCleaner._ob_adjust_param, (ob,)
)
if avoider.dimensions != (0, 0)
)
new_containers = tuple(
ParameterList(
Expand All @@ -313,7 +336,7 @@ def small_ob_inferral(mappling: MappedTiling) -> MappedTiling:
return new_mappling

@staticmethod
@reg(7)
@reg(8)
def forward_map_parameter_gcps_from_avoiders(
mappling: MappedTiling,
) -> MappedTiling:
Expand Down Expand Up @@ -362,7 +385,7 @@ def forward_map_parameter_gcps_from_avoiders(
return MappedTiling(new_base, new_avoiders, containers, enumerators)

@staticmethod
@reg(9)
@reg(10)
def forward_map_parameter_gcps_from_containers(
mappling: MappedTiling,
) -> MappedTiling:
Expand Down Expand Up @@ -432,7 +455,7 @@ def reap_blank(mappling: MappedTiling) -> MappedTiling:
)

@staticmethod
@reg(13)
@reg(14)
def remove_blank_rows_and_cols_params(mappling: MappedTiling) -> MappedTiling:
"""Deletes all rows and cols in the parameters which have no obs or reqs,
ignoring point rows and columns and obstructions which are already on the
Expand Down
33 changes: 0 additions & 33 deletions mapplings/cleaners/parameter_cleaner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
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
from mapplings import Parameter

Expand Down Expand Up @@ -131,38 +130,6 @@ def to_remove(
return Parameter(Tiling([], [], (0, 0)), RowColMap({}, {}))
return param.delete_rows_and_columns(cols_to_remove, rows_to_remove)

@staticmethod
@reg(4, run_on_enumerators=False)
def unplace_points(param: Parameter) -> Parameter:
"""Unplaces all possible points in the parameter"""
algo = PartialUnplacement(param.ghost)
points = param.single_value_cells()
cells, cols, rows = set[tuple[int, int]](), set[int](), set[int]()
for cell in points:
valid = algo.cell_in_valid_region(cell)
if valid[0] and param.col_map[cell[0] - 1] == param.col_map[cell[0] + 1]:
cells.add(cell)
cols.add(cell[0])
if valid[1] and param.row_map[cell[1] - 1] == param.row_map[cell[1] + 1]:
cells.add(cell)
rows.add(cell[1])
unplace_cols, unplace_rows = algo.fusable_check(cells, cols, rows)
if not (unplace_cols or unplace_rows):
return param
new_ghost = algo.unplace(unplace_cols, unplace_rows)
col_preimages, row_preimages = algo.adjustment_map(
unplace_cols, unplace_rows
).preimage_map()
new_col_map = {
i: param.col_map[col_preimages[i][0]]
for i in range(new_ghost.dimensions[0])
}
new_row_map = {
i: param.row_map[row_preimages[i][0]]
for i in range(new_ghost.dimensions[1])
}
return Parameter(new_ghost, RowColMap(new_col_map, new_row_map))

@staticmethod
@reg(3, run_on_enumerators=False)
def insert_blank_size_1(param: Parameter) -> Parameter:
Expand Down
94 changes: 94 additions & 0 deletions mapplings/cleaners/unplacement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Contains the ParamUnplacement class"""

from itertools import product
from typing import Iterator
from functools import cached_property

from gridded_cayley_permutations import RowColMap, GriddedCayleyPerm
from gridded_cayley_permutations.simplify_obstructions_and_requirements import (
SimplifyObstructionsAndRequirements as Simplify,
)
from gridded_cayley_permutations.unplacement import PartialUnplacement
from mapplings import Parameter, MappedTiling


class ParamUnplacement(PartialUnplacement):
"""A class for unplacing point rows and cols in a parameter"""

def __init__(self, param: Parameter, parent_mappling: MappedTiling):
self.param = param
self.base = parent_mappling
self.base_obs = parent_mappling.obstructions
super().__init__(param.ghost)

def implied_point_obs(self) -> Iterator[GriddedCayleyPerm]:
"""Finds point obs in the parameter that are implied by the base mappling"""
for cells in product(self.param.empty_cells(), self.param.positive_cells()):
pos = sorted(cells)
pattern = (pos[0][1] > pos[1][1], pos[1][1] > pos[0][1])
gcp = GriddedCayleyPerm(pattern, pos)
if self.param.map.map_gridded_cperm(gcp) not in self.base_obs:
yield GriddedCayleyPerm((0,), (cells[0],))

@cached_property
def expected_obs(self) -> set[GriddedCayleyPerm]:
unsimplified = tuple(super().expected_obs | set(self.implied_point_obs()))
algo = Simplify(unsimplified, tuple(tuple()), self.param.dimensions)
algo.simplify()
return set(algo.obstructions)

def check_cols_and_rows(
self, check_cols: set[int], check_rows: set[int]
) -> tuple[set[int], set[int]]:
"""Filters the input cols and rows to only include those which can be unplaced."""
valid_cols = {
col
for col in check_cols & self.positive_cols
if (0 < col < self.dimensions[0] - 1)
}
valid_rows = {
row
for row in check_rows & self.positive_rows
if (0 < row < self.dimensions[1] - 1)
}
valid_cols = {
col
for col in valid_cols
if self.param.col_map[col - 1] == self.param.col_map[col + 1]
}
valid_rows = {
row
for row in valid_rows
if self.param.row_map[row - 1] == self.param.row_map[row + 1]
}
valid_cols = set(filter(self.col_fuse_check, valid_cols))
valid_rows = set(filter(self.row_fuse_check, valid_rows))
return valid_cols, valid_rows

def param_unplace(
self, unplace_cols: set[int], unplace_rows: set[int]
) -> Parameter:
"""Unplaces rows and cols in the ghost and creates a new row col map"""
if not any((unplace_cols, unplace_rows)):
return self.param
new_ghost = self.unplace(unplace_cols, unplace_rows)

col_preimages, row_preimages = self.adjustment_map(
unplace_cols, unplace_rows
).preimage_map()

new_col_map = {
i: self.param.col_map[col_preimages[i][0]]
for i in range(new_ghost.dimensions[0])
}
new_row_map = {
i: self.param.row_map[row_preimages[i][0]]
for i in range(new_ghost.dimensions[1])
}
return Parameter(new_ghost, RowColMap(new_col_map, new_row_map))

def auto_unplace(self):
"""Does all valid unplacements for the tiling's point cells"""
temp = self.param_unplace(set(), self.find_cols_and_rows()[1])
new_algo = ParamUnplacement(temp, self.base)
return new_algo.param_unplace(new_algo.find_cols_and_rows()[0], set())
7 changes: 6 additions & 1 deletion mapplings/mapped_tiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from .parameter import Parameter
from .parameter_list import ParameterList


Objects = DefaultDict[tuple[int, ...], List[GriddedCayleyPerm]]
Cell = tuple[int, int]

Expand All @@ -42,6 +41,12 @@ def __init__(
self.containing_parameters = tuple(sorted(containing_parameters))
self.enumerating_parameters = tuple(sorted(enumerating_parameters))
self.tiling = tiling

if any(param.dimensions == (0, 0) for param in avoiding_parameters):
self.tiling = Tiling.empty_tiling()
self.avoiding_parameters = ParameterList()
self.containing_parameters = tuple()
self.enumerating_parameters = tuple()
super().__init__(
tiling.obstructions, tiling.requirements, tiling.dimensions, simplify
)
Expand Down
5 changes: 5 additions & 0 deletions mapplings/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def injective_cells(self) -> set[Cell]:
)
return set(product(inj_cols, inj_rows))

def add_obstructions(self, gcps: Iterable[GriddedCayleyPerm]) -> "Parameter":
"""Returns a new parameter with the obstructions added to the ghost."""
new_ghost = super().add_obstructions(gcps)
return Parameter(new_ghost, self.map)

def preimage_of_gcp(self, gcperm: GriddedCayleyPerm) -> Iterator[GriddedCayleyPerm]:
"""Returns the preimage of a gridded cayley permutation"""
for gcp in self.map.preimage_of_gridded_cperm(gcperm):
Expand Down
3 changes: 3 additions & 0 deletions mapplings/parameter_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,6 @@ def __lt__(self, other: object):
if isinstance(other, ParameterList):
return tuple(sorted(self)) < tuple(sorted(other))
return NotImplemented

def __getitem__(self, key):
return sorted(self)[key]
1 change: 0 additions & 1 deletion mapplings/strategies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@

from .mapped_tilescope import MappedTileScopePack


__all__ = ["MappedTileScopePack"]
__version__ = "0.1.0"
Loading
Loading