Skip to content

Commit

Permalink
Merge pull request #46 from PermutaTriangle/req-placements
Browse files Browse the repository at this point in the history
Req placements
  • Loading branch information
enadeau authored Jan 30, 2020
2 parents ddcd6f5 + 53580cc commit fb2e75f
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 442 deletions.
60 changes: 45 additions & 15 deletions tests/strategies/equivalence_strategies/test_point_placements.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from permuta import Perm
from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST, DIRS
from tilescopethree.strategies import point_placement, requirement_placement
from tilescopethree.strategies.equivalence_strategies.point_placements import \
place_point_of_requirement
from tilings import Obstruction, Requirement, Tiling

pytest_plugins = [
Expand All @@ -15,16 +13,16 @@

def test_point_placement(diverse_tiling, no_point_tiling):
strats = list(point_placement(diverse_tiling))
assert len(strats) == 3 * len(DIRS)

assert len(strats) == 5 * len(DIRS)
strats = list(requirement_placement(no_point_tiling))
assert len(strats) == 3 * len(DIRS)
assert len(strats) == 9 * len(DIRS)
strats = list(point_placement(no_point_tiling))
assert len(strats) == 0
assert len(strats) == 3 * len(DIRS)


def test_place_point_of_requirement_point_only(diverse_tiling):
tiling = place_point_of_requirement(diverse_tiling, 0, 0, DIR_WEST)
tiling = diverse_tiling.place_point_of_gridded_permutation(
diverse_tiling.requirements[0][0], 0, DIR_WEST)
assert tiling == Tiling(
obstructions=[
Obstruction(Perm((0,)), [(1, 0)]),
Expand Down Expand Up @@ -53,13 +51,43 @@ def test_place_point_of_requirement_point_only(diverse_tiling):
Requirement(Perm((0, 2, 1)), [(0, 3), (0, 4), (1, 4)]),
Requirement(Perm((0, 2, 1)), [(0, 3), (0, 4), (3, 4)])]])

assert tiling == place_point_of_requirement(diverse_tiling, 0, 0, DIR_EAST)
assert tiling == place_point_of_requirement(
diverse_tiling, 0, 0, DIR_NORTH)
assert tiling == place_point_of_requirement(
diverse_tiling, 0, 0, DIR_SOUTH)
assert tiling == diverse_tiling.place_point_of_gridded_permutation(
diverse_tiling.requirements[0][0], 0, DIR_EAST)
assert tiling == diverse_tiling.place_point_of_gridded_permutation(
diverse_tiling.requirements[0][0], 0, DIR_NORTH)
assert tiling == diverse_tiling.place_point_of_gridded_permutation(
diverse_tiling.requirements[0][0], 0, DIR_SOUTH)

tiling = place_point_of_requirement(diverse_tiling, 1, 0, DIR_WEST)
print(Tiling(
obstructions=[
Obstruction(Perm((0,)), [(2, 0)]),
Obstruction(Perm((0,)), [(2, 2)]),
Obstruction(Perm((0, 1)), [(1, 0), (1, 0)]),
Obstruction(Perm((0, 1)), [(1, 0), (1, 2)]),
Obstruction(Perm((0, 1)), [(1, 2), (1, 2)]),
Obstruction(Perm((0, 1)), [(3, 1), (3, 1)]),
Obstruction(Perm((0, 1)), [(2, 3), (2, 3)]),
Obstruction(Perm((0, 1)), [(2, 3), (4, 3)]),
Obstruction(Perm((0, 1)), [(4, 3), (4, 3)]),
Obstruction(Perm((1, 0)), [(1, 0), (1, 0)]),
Obstruction(Perm((1, 0)), [(1, 2), (1, 0)]),
Obstruction(Perm((1, 0)), [(1, 2), (1, 2)]),
Obstruction(Perm((1, 0)), [(3, 1), (3, 1)]),
Obstruction(Perm((1, 0)), [(2, 3), (2, 3)]),
Obstruction(Perm((1, 0)), [(2, 3), (4, 3)]),
Obstruction(Perm((1, 0)), [(4, 3), (4, 3)]),
Obstruction(Perm((0, 1, 2)), [(0, 0), (1, 3), (1, 3)]),
Obstruction(Perm((0, 2, 3, 1)), [(0, 2), (1, 3), (1, 3), (4, 2)])],
requirements=[
[Requirement(Perm((0,)), [(3, 1)])],
[Requirement(Perm((0,)), [(1, 0)]),
Requirement(Perm((0,)), [(1, 2)])],
[Requirement(Perm((0,)), [(2, 3)]),
Requirement(Perm((0,)), [(4, 3)])],
[Requirement(Perm((1, 0)), [(0, 4), (0, 3)]),
Requirement(Perm((0, 2, 1)), [(0, 3), (0, 4), (1, 4)])]]))
tiling = diverse_tiling.place_point_of_gridded_permutation(
diverse_tiling.requirements[1][0], 0, DIR_WEST)
assert tiling == Tiling(
obstructions=[
Obstruction(Perm((0,)), [(2, 0)]),
Expand Down Expand Up @@ -90,14 +118,16 @@ def test_place_point_of_requirement_point_only(diverse_tiling):
Requirement(Perm((0, 2, 1)), [(0, 3), (0, 4), (1, 4)])]])

tiling = Tiling(requirements=[[Requirement(Perm((0,)), [(0, 0)])]])
assert place_point_of_requirement(tiling, 0, 0, DIR_SOUTH) == Tiling(
assert tiling.place_point_of_gridded_permutation(tiling.requirements[0][0],
0, DIR_SOUTH) == Tiling(
obstructions=[Obstruction(Perm((0, 1)), [(0, 0), (0, 0)]),
Obstruction(Perm((1, 0)), [(0, 0), (0, 0)])],
requirements=[[Requirement(Perm((0,)), [(0, 0)])]])


def test_place_point_of_requirement(no_point_tiling):
tiling = place_point_of_requirement(no_point_tiling, 2, 1, DIR_WEST)
tiling = no_point_tiling.place_point_of_gridded_permutation(
no_point_tiling.requirements[2][0], 1, DIR_WEST)
tiling2 = Tiling(
obstructions=[
Obstruction(Perm((0, 1)), [(0, 1), (1, 3)]),
Expand Down
4 changes: 4 additions & 0 deletions tilescopethree/strategies/batch_strategies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
all_requirement_insertions, all_row_insertions,
root_requirement_insertion)
from .list_requirement_placements import (col_placements,
partial_col_placements,
partial_row_and_col_placements,
partial_row_placements,
requirement_list_placement,
row_and_col_placements,
row_placements)
from .requirement_corroboration import requirement_corroboration
from .targeted_cell_insertion import targeted_cell_insertion
Original file line number Diff line number Diff line change
@@ -1,208 +1,52 @@
from itertools import chain
from itertools import product

from comb_spec_searcher import Rule
from permuta import Perm
from permuta.misc import DIR_EAST, DIR_NONE, DIR_NORTH, DIR_SOUTH, DIR_WEST
from tilings import Obstruction, Requirement, Tiling
from comb_spec_searcher import BatchRule
from permuta.misc import DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST
from tilings.algorithms import RequirementPlacement


def requirement_list_placement(tiling, **kwargs):
"""Places all requirements on the tiling in every direction."""
for req in tiling.requirements:
for direction in [DIR_NORTH, DIR_SOUTH, DIR_EAST, DIR_WEST]:
tilings = place_requirement_list(tiling, req, direction)
if tilings is None:
continue
yield Rule(
formal_step=("Place requirement {} in direction {}."
"".format(req, direction)),
comb_classes=tilings,
ignore_parent=False,
possibly_empty=[True for _ in tilings],
inferable=[True for _ in tilings],
workable=[True for _ in tilings],
constructor='disjoint')
req_placement = RequirementPlacement(tiling)
directions = (DIR_EAST, DIR_NORTH, DIR_SOUTH, DIR_WEST)
for req, direction in product(tiling.requirements, directions):
yield BatchRule(req_placement.place_point_of_req_list(req, direction),
"Inserting {} point of requirement list ({})".format(
req_placement._direction_string(direction),
", ".join(str(r) for r in req)))


def row_placements(tiling, row=True, positive=True, regions=False, **kwargs):
"""Places the points in a row (or col if row=False) in the given
direction."""
if row:
x = tiling.dimensions[1]
y = tiling.dimensions[0]
only_cell_in_col = tiling.only_cell_in_col
directions = [DIR_NORTH, DIR_SOUTH]
else:
x = tiling.dimensions[0]
y = tiling.dimensions[1]
only_cell_in_col = tiling.only_cell_in_row
directions = [DIR_EAST, DIR_WEST]
def row_placements(tiling, **kwargs):
yield from RequirementPlacement(tiling).all_row_placement_rules()

rows = range(x)
if kwargs.get("index") is not None:
rows = [kwargs.get("index")]
if kwargs.get("direction") is not None:
directions = [kwargs["direction"]]
if regions:
forward_maps = []

direction = kwargs.get('direction')
if direction is not None:
if row:
if direction == DIR_NORTH:
directions = [DIR_NORTH]
elif direction == DIR_SOUTH:
directions = [DIR_SOUTH]
else:
raise ValueError("Can't place rows in direction.")
else:
if direction == DIR_EAST:
directions = [DIR_EAST]
elif direction == DIR_WEST:
directions = [DIR_WEST]
else:
raise ValueError("Can't place cols in direction.")
def col_placements(tiling, **kwargs):
yield from RequirementPlacement(tiling).all_col_placement_rules()

for i in rows:
place = True
cells_in_row = []
for j in range(y):
cell = (j, i) if row else (i, j)
if positive and cell not in tiling.positive_cells:
place = False
break
cells_in_row.append(cell)
if place:
if (len(cells_in_row) != 1 or
not cells_in_row[0] in tiling.point_cells or
not only_cell_in_col(cells_in_row[0])):
req_list = tuple(Requirement(Perm((0,)), (cell,))
for cell in cells_in_row)
if not positive:
"""Adding the empty row case."""
empty_row_tiling = Tiling(tiling.obstructions +
tuple(Obstruction(Perm((0,)),
(cell,))
for cell in cells_in_row),
tiling.requirements)
if regions:
forward_maps.append({c: frozenset([c])
for c in tiling.active_cells})
for direction in directions:
if regions:
tilings, placed_maps = place_requirement_list(
tiling, req_list, direction,
regions=regions)
if not positive:
tilings = [empty_row_tiling] + tilings
forward_maps.extend(placed_maps)
yield tilings, forward_maps
else:
tilings = place_requirement_list(tiling, req_list,
direction)
if not positive:
tilings = [empty_row_tiling] + tilings
yield Rule(
formal_step=("Placing {} {} in direction {}."
"".format("row" if row else "col",
i, direction,
i, direction, int(positive))),
comb_classes=tilings,
ignore_parent=False,
possibly_empty=[True for _ in tilings],
inferable=[True for _ in tilings],
workable=[True for _ in tilings],
constructor='disjoint')

def row_and_col_placements(tiling, **kwargs):
req_placements = RequirementPlacement(tiling)
yield from req_placements.all_row_placement_rules()
yield from req_placements.all_col_placement_rules()

def col_placements(tiling, **kwargs):
yield from row_placements(tiling, row=False, **kwargs)

def partial_row_placements(tiling, **kwargs):
req_placements = (RequirementPlacement(tiling, own_row=False),
RequirementPlacement(tiling, own_col=False))
for req_placement in req_placements:
yield from req_placement.all_row_placement_rules()

def place_requirement_list(tiling, req_list, direction, regions=False):
"""Return the list of tilings obtained by placing the direction-most point
of a requirement list. This represents a batch strategy, where the
direction-most point of each requirement in the list is placed."""
# Compute the points furthest in the given direction.
min_points = minimum_points(req_list, direction)
if len([c for _, c in min_points]) != len(set([c for _, c in min_points])):
# Can't handle list requirements with more than req farthest in the
# direction in same cell.
return None
# For each tiling, compute the tiling where this point is placed furthest
# in that direction.
res = []
if regions:
forward_maps = []
for (idx, cell), req in zip(min_points, req_list):
# Placing the forced occurrence of the point in the requirement
new_req, forced_obstructions = req.place_forced_point(idx, direction)
assert len(new_req) == 1
# Add the forced obstruction to ensure no other requirement has a point
# further in that direction.
forced_obstructions = (forced_obstructions +
list(chain.from_iterable(
r.other_req_forced_point(cell, direction)
for r in req_list if r != req)))
# New indices of the point
point_cell = (cell[0] + 1, cell[1] + 1)
# The set of new obstructions, consisting of the forced obstructions,
# other obstructions where the point placement has been taken into
# account and the 12, 21 in the cell.
newobs = forced_obstructions + list(chain.from_iterable(
ob.place_point(cell, DIR_NONE) for ob in tiling.obstructions)) + [
Obstruction.single_cell(Perm((0, 1)), point_cell),
Obstruction.single_cell(Perm((1, 0)), point_cell)]
# The rest of the requirements
other_reqs = [reqs for reqs in tiling.requirements if reqs != req_list]
# The new requirements, consisting of the requirement with the point
# placed, other requirements where point placement has been taken into
# account and the point requirement in the cell.
newreqs = [list(chain.from_iterable(r.place_point(cell, DIR_NONE)
for r in reqs))
for reqs in other_reqs] + [new_req] + [
[Requirement.single_cell(Perm((0,)), point_cell)]]
placed_tiling = Tiling(newobs, newreqs)
res.append(placed_tiling)
if regions:
def cell_map(c):
mindex, minval = c
maxdex = mindex + 1
maxval = minval + 1
if mindex >= cell[0]:
maxdex += 2
if minval >= cell[1]:
maxval += 2
if mindex > cell[0]:
mindex += 2
if minval > cell[1]:
minval += 2
return frozenset([(x, y) for x in range(mindex, maxdex)
for y in range(minval, maxval)])
forward_maps.append({c: cell_map(c) for c in tiling.active_cells})
if regions:
return res, forward_maps
else:
return res

def partial_col_placements(tiling, **kwargs):
req_placements = (RequirementPlacement(tiling, own_row=False),
RequirementPlacement(tiling, own_col=False))
for req_placement in req_placements:
yield from req_placement.all_col_placement_rules()

def minimum_points(req_list, direction):
"""Return a list of tuple containing the index and cell of the point in the
requirement furthest to the geiven direction."""
res = []
if direction == DIR_WEST:
res = [(0, req.pos[0]) for req in req_list]
elif direction == DIR_EAST:
res = [(len(req) - 1, req.pos[-1]) for req in req_list]
elif direction == DIR_SOUTH:
for req in req_list:
mindex = req.patt.index(0)
res.append((mindex, req.pos[mindex]))
elif direction == DIR_NORTH:
for req in req_list:
maxdex = req.patt.index(0)
res.append((maxdex, req.pos[maxdex]))
else:
raise ValueError("Must choose north, south, east or west.")

return res
def partial_row_and_col_placements(tiling, **kwargs):
req_placements = (RequirementPlacement(tiling, own_row=False),
RequirementPlacement(tiling, own_col=False))
for req_placement in req_placements:
yield from req_placement.all_row_placement_rules()
yield from req_placement.all_col_placement_rules()
3 changes: 2 additions & 1 deletion tilescopethree/strategies/equivalence_strategies/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from .fusion_with_interleaving import fusion_with_interleaving
from .partial_point_placements import (partial_point_placement,
partial_requirement_placement)
from .point_placements import point_placement, requirement_placement
from .point_placements import (all_placements, point_placement,
requirement_placement)
21 changes: 7 additions & 14 deletions tilescopethree/strategies/equivalence_strategies/isolate_points.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
from comb_spec_searcher import EquivalenceRule
from permuta.misc import DIR_WEST
from tilescopethree.strategies.batch_strategies.point_placements import \
place_point_of_requirement
from tilings.algorithms import RequirementPlacement


def point_isolations(tiling, **kwargs):
point_cells = tiling.point_cells
for ri, reqs in enumerate(tiling.requirements):
if len(reqs) > 1:
continue
cell = reqs[0].is_point_perm()
if cell is None or cell not in point_cells:
continue
yield EquivalenceRule(
formal_step=("Isolating point at {} into its own row and "
"column").format(cell),
# Direction does not matter
comb_class=place_point_of_requirement(tiling, ri, 0, DIR_WEST))
req_placement = RequirementPlacement(tiling)
for cell in tiling.point_cells:
if not tiling.only_cell_in_row_and_col():
isolated_tiling = req_placement.place_point_in_cell(cell, DIR_WEST)
yield EquivalenceRule("Isolate point at cell {}.".format(cell),
isolated_tiling)
Loading

0 comments on commit fb2e75f

Please sign in to comment.