Skip to content

Commit 9044cba

Browse files
authored
Merge pull request #45 from PermutaTriangle/component-fusion
Fusion with interleaving
2 parents cc45fe8 + c54e496 commit 9044cba

File tree

4 files changed

+82
-165
lines changed

4 files changed

+82
-165
lines changed

tests/strategies/equivalence_strategies/test_fusion.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,6 @@ def big_tiling():
5454
return t
5555

5656

57-
@pytest.fixture
58-
def row_fusion(small_tiling):
59-
return Fusion(small_tiling, row_idx=0)
60-
61-
62-
@pytest.fixture
63-
def col_fusion(small_tiling):
64-
return Fusion(small_tiling, col_idx=0)
65-
66-
6757
def test_fusion(small_tiling, big_tiling):
6858
assert len(list(fusion(big_tiling))) == 0
6959
small_tiling_rules = list(fusion(small_tiling))
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pytest
2+
3+
from permuta import Perm
4+
from tilescopethree.strategies import fusion_with_interleaving
5+
from tilings import Obstruction, Tiling
6+
7+
8+
@pytest.fixture
9+
def tiling1():
10+
t = Tiling(obstructions=[
11+
Obstruction(Perm((1, 0)), ((0, 1), (1, 1))),
12+
Obstruction(Perm((1, 0)), ((0, 1), (0, 1))),
13+
Obstruction(Perm((1, 0)), ((0, 1), (1, 0))),
14+
Obstruction(Perm((1, 0)), ((0, 1), (0, 0))),
15+
Obstruction(Perm((1, 0)), ((0, 0), (0, 0))),
16+
Obstruction(Perm((1, 0)), ((0, 0), (1, 0))),
17+
Obstruction(Perm((1, 0)), ((1, 0), (1, 0))),
18+
Obstruction(Perm((1, 0)), ((1, 1), (1, 0))),
19+
Obstruction(Perm((1, 0)), ((1, 1), (1, 1)))
20+
])
21+
return t
22+
23+
24+
@pytest.fixture
25+
def tiling2():
26+
t = Tiling(obstructions=[
27+
Obstruction(Perm((0, 1)), ((0, 0), (1, 0))),
28+
Obstruction(Perm((0, 2, 1)), ((0, 0), (0, 0), (0, 0))),
29+
Obstruction(Perm((0, 2, 1)), ((1, 0), (1, 0), (1, 0))),
30+
Obstruction(Perm((0, 2, 1, 3)), ((0, 0), (0, 0), (2, 0), (2, 0))),
31+
Obstruction(Perm((0, 2, 1, 3)), ((0, 0), (2, 0), (2, 0), (2, 0))),
32+
Obstruction(Perm((0, 2, 1, 3)), ((1, 0), (1, 0), (2, 0), (2, 0))),
33+
Obstruction(Perm((0, 2, 1, 3)), ((1, 0), (2, 0), (2, 0), (2, 0))),
34+
Obstruction(Perm((0, 2, 1, 3)), ((2, 0), (2, 0), (2, 0), (2, 0)))
35+
])
36+
return t
37+
38+
39+
def test_fusion_with_interleaving(tiling1, tiling2):
40+
assert len(list(fusion_with_interleaving(tiling1))) == 0
41+
assert len(list(fusion_with_interleaving(tiling2))) == 1

tests/test_tilescope.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
from comb_spec_searcher import ProofTree
55
from tilescopethree import TileScopeTHREE
6-
from tilescopethree.strategy_packs_v2 import (point_placements,
7-
point_placements_fusion)
6+
from tilescopethree.strategy_packs_v2 import (
7+
point_placements, point_placements_fusion,
8+
point_placements_fusion_with_interleaving,
9+
row_and_col_placements_fusion_with_interleaving_fusion)
810

911

1012
@pytest.mark.timeout(20)
@@ -23,3 +25,32 @@ def test_123():
2325
searcher = TileScopeTHREE('123', point_placements_fusion)
2426
t = searcher.auto_search(smallest=True)
2527
assert isinstance(t, ProofTree)
28+
29+
30+
@pytest.mark.timeout(20)
31+
def test_1342_1423():
32+
searcher = TileScopeTHREE('1342_1423',
33+
point_placements_fusion_with_interleaving)
34+
t = searcher.auto_search(smallest=True)
35+
t.number_of_nodes() == 14
36+
assert isinstance(t, ProofTree)
37+
38+
39+
@pytest.mark.timeout(20)
40+
def test_1324():
41+
searcher = TileScopeTHREE(
42+
'1324',
43+
row_and_col_placements_fusion_with_interleaving_fusion
44+
)
45+
t = searcher.auto_search(smallest=True)
46+
t.number_of_nodes() == 14
47+
num_fusion = 0
48+
num_comp_fusion = 0
49+
for node in t.nodes():
50+
if 'Fuse' in node.formal_step:
51+
num_fusion += 1
52+
if 'Component' in node.formal_step:
53+
num_comp_fusion += 1
54+
assert num_fusion == 1
55+
assert num_comp_fusion == 1
56+
assert isinstance(t, ProofTree)
Lines changed: 8 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,17 @@
11
"""The more general fusion strategy. Fuse two rows if actually one row where
22
you can draw a line somewhere."""
3-
from collections import defaultdict
4-
53
from comb_spec_searcher import Rule
6-
from permuta import Perm
7-
from tilings import Obstruction, Tiling
4+
from tilings.algorithms import ComponentFusion
5+
6+
from .fusion import general_fusion_iterator
87

98

109
def fusion_with_interleaving(tiling, **kwargs):
11-
"""Yield rules found by fusing rows and columns of a tiling, where the
10+
"""
11+
Yield rules found by fusing rows and columns of a tiling, where the
1212
unfused tiling obtained by drawing a line through certain heights/indices
13-
of the row/column."""
13+
of the row/column.
14+
"""
1415
if tiling.requirements:
1516
return
16-
bases = tiling.cell_basis()
17-
for row_index in range(tiling.dimensions[1] - 1):
18-
if fusable(tiling, row_index, bases, True, **kwargs):
19-
yield Rule(("Fuse rows fancily {} and {}|{}|."
20-
"").format(row_index, row_index + 1, row_index),
21-
[fuse_tiling(tiling, row_index, True)],
22-
inferable=[True], workable=[True],
23-
possibly_empty=[False], constructor='other')
24-
for col_index in range(tiling.dimensions[0] - 1):
25-
if fusable(tiling, col_index, bases, False, **kwargs):
26-
# if not original_fusable(tiling, col_index, False):
27-
# print("================================")
28-
# print("On the tiling:")
29-
# print(tiling)
30-
# print("Column {} is fancy fusable but not ordinary fusable.
31-
# Explain.".format(col_index))
32-
yield Rule(("Fuse columns fancily {} and {}|{}|."
33-
"").format(col_index, col_index + 1, col_index),
34-
[fuse_tiling(tiling, col_index, False)],
35-
inferable=[True], workable=[True],
36-
possibly_empty=[False], constructor='other')
37-
# elif original_fusable(tiling, col_index, False):
38-
# print("================================")
39-
# print("On the tiling:")
40-
# print(tiling)
41-
# print("Column {} is ordinary fusable but not fancy fusable.
42-
# Explain.".format(col_index))
43-
44-
45-
def fusable(tiling, row_index, bases, row=True, **kwargs):
46-
"""Return true if adjacent rows can be viewed as one row where you draw a
47-
horizontal line through the permutation."""
48-
first_row = (tiling.cells_in_row(row_index)
49-
if row else tiling.cells_in_col(row_index))
50-
# only consider rows of size one
51-
if len(first_row) > 1:
52-
return False
53-
second_row = (tiling.cells_in_row(row_index + 1)
54-
if row else tiling.cells_in_col(row_index + 1))
55-
# adjacent cells in rows must not be empty
56-
if len(first_row) != len(second_row):
57-
return False
58-
first_cell = list(first_row)[0]
59-
second_cell = list(second_row)[0]
60-
# ensure the other cell is adjacent
61-
if ((row and first_cell[0] != second_cell[0]) or
62-
(not row and first_cell[1] != second_cell[1])):
63-
return False
64-
# ensure other cells basis is the same, and not the same as the root basis
65-
if (bases[first_cell][0] != bases[second_cell][0]):
66-
return False
67-
obstructions_to_add = []
68-
for ob in tiling.obstructions:
69-
# for each obstruction that occupies the first cell, draw a line
70-
# through it in every way so that it crosses to the secoond cell
71-
# TODO: Should we be worried about obstruction going only to the
72-
# second cell?
73-
if ob.occupies(first_cell):
74-
# ignore the obstructions that imply skew or sum components
75-
if len(ob) == 2 and ob.occupies(second_cell):
76-
continue
77-
# the point in the first cell
78-
in_cell = [(idx, val) for idx, val in enumerate(ob.patt)
79-
if ob.pos[idx] == first_cell]
80-
if row:
81-
in_cell = sorted(in_cell, key=lambda x: (x[1], x[0]))
82-
special_cell = second_cell
83-
# place i points in bottom cell, rest in top.
84-
for i in range(len(in_cell)):
85-
maxdex = [point[0] for point in in_cell[i:]]
86-
pos = [special_cell if i in maxdex else c
87-
for i, c in enumerate(ob.pos)]
88-
obstructions_to_add.append(Obstruction(ob.patt, pos))
89-
if ob.occupies(second_cell):
90-
in_cell = [(idx, val) for idx, val in enumerate(ob.patt)
91-
if ob.pos[idx] == second_cell]
92-
if row:
93-
in_cell = sorted(in_cell, key=lambda x: -x[1])
94-
else:
95-
in_cell = sorted(in_cell, key=lambda x: -x[0])
96-
special_cell = first_cell
97-
# place i points in bottom cell, rest in top.
98-
for i in range(len(in_cell)):
99-
maxdex = [point[0] for point in in_cell[i:]]
100-
pos = [special_cell if i in maxdex else c
101-
for i, c in enumerate(ob.pos)]
102-
obstructions_to_add.append(Obstruction(ob.patt, pos))
103-
104-
# if the tiling is unchanged, then the previous obstruction imply all those
105-
# obstructions that needed to be added, and therefore we can think of this
106-
# as one row with a line drawn through it
107-
if tiling == Tiling(list(tiling.obstructions) + obstructions_to_add,
108-
tiling.requirements):
109-
# return True
110-
if (Obstruction(Perm((0, 1)),
111-
(first_cell, second_cell)) in tiling.obstructions or
112-
Obstruction(Perm((0, 1)),
113-
(second_cell, first_cell)) in tiling.obstructions or
114-
Obstruction(Perm((1, 0)),
115-
(first_cell, second_cell)) in tiling.obstructions or
116-
Obstruction(Perm((1, 0)),
117-
(second_cell, first_cell)) in tiling.obstructions):
118-
return True
119-
120-
121-
def fuse_tiling(tiling, row_index, row=True, **kwargs):
122-
"""
123-
Return the tiling where rows 'row_index' and 'row_index + 1' are fused.
124-
125-
If row=False, then it does the same for columns.
126-
127-
(Note unlike fusion file, we ignore obstruction using the other cell)
128-
"""
129-
fused_obstructions = [fuse_gridded_perm(ob, row_index, row)
130-
for ob in tiling.obstructions
131-
if ((row and
132-
all(c[1] != row_index + 1 for c in ob.pos)) or
133-
(not row and
134-
all(c[0] != row_index + 1 for c in ob.pos)))]
135-
fused_requirements = [[fuse_gridded_perm(req, row_index, row)
136-
for req in req_list]
137-
for req_list in tiling.requirements]
138-
fused_tiling = Tiling(fused_obstructions, fused_requirements)
139-
if kwargs.get('regions', False):
140-
cell_to_region = {}
141-
for cell in tiling.active_cells:
142-
x, y = cell
143-
if row and y > row_index:
144-
y -= 1
145-
elif not row and x > row_index:
146-
x -= 1
147-
cell_to_region[cell] = set([(x, y)])
148-
return ([fused_tiling], [cell_to_region])
149-
return fused_tiling
150-
151-
152-
def fuse_gridded_perm(gp, row_index, row=True):
153-
"""Fuses rows 'row_index' and 'row_index + 1'."""
154-
fused_pos = []
155-
for cell in gp.pos:
156-
x, y = cell
157-
if row and y > row_index:
158-
y -= 1
159-
elif not row and x > row_index:
160-
x -= 1
161-
fused_pos.append((x, y))
162-
return gp.__class__(gp.patt, fused_pos)
17+
yield from general_fusion_iterator(tiling, ComponentFusion)

0 commit comments

Comments
 (0)