Skip to content

Commit 9b1b324

Browse files
authored
Merge pull request #40 from PermutaTriangle/factor
Factor
2 parents 96ed92a + 3cee2ac commit 9b1b324

File tree

4 files changed

+72
-186
lines changed

4 files changed

+72
-186
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ test=pytest
44
[tool:pytest]
55
testpaths = tilescopethree tests
66
addopts = --pep8 --isort
7-
--cov=permuta --cov-report=term-missing
7+
--cov=tilescopethree --cov-report=term-missing
88
--doctest-modules --doctest-ignore-import-errors
99

1010
[tool:isort]

tests/strategies/decomposition_strategies/test_factor.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,53 @@
11
from permuta import Perm
2-
from tilescopethree.strategies import factor
2+
from tilescopethree.strategies import (
3+
factor, factor_with_interleaving, factor_with_monotone_interleaving,
4+
unions_of_factor, unions_of_factor_with_interleaving,
5+
unions_of_factor_with_monotone_interleaving)
6+
from tilescopethree.strategies.decomposition_strategies.factor import \
7+
general_factor
38
from tilings import Obstruction, Requirement, Tiling
9+
from tilings.algorithms import (Factor, FactorWithInterleaving,
10+
FactorWithMonotoneInterleaving)
411

512
pytest_plugins = [
613
'tests.fixtures.simple_tiling',
714
'tests.fixtures.diverse_tiling',
15+
'tests.fixtures.no_point_tiling',
816
]
917

1018

19+
def test_general_factor(simple_tiling, diverse_tiling):
20+
assert len(list(general_factor(simple_tiling, Factor))) == 0
21+
assert (len(list(general_factor(simple_tiling,
22+
FactorWithMonotoneInterleaving))) == 0)
23+
assert (len(list(general_factor(simple_tiling,
24+
FactorWithInterleaving))) == 0)
25+
assert (len(list(general_factor(diverse_tiling,
26+
FactorWithInterleaving))) == 1)
27+
# Test union param
28+
strats = list(general_factor(diverse_tiling,
29+
FactorWithInterleaving,
30+
union=True))
31+
assert len(strats) == 14
32+
assert sum(1 for s in strats if 'unions' in s.formal_step) == 13
33+
assert sum(1 for s in strats if all(s.workable)) == 1
34+
assert sum(1 for s in strats if not any(s.workable)) == 13
35+
# Test union param with workable to False
36+
strats = list(general_factor(diverse_tiling,
37+
FactorWithInterleaving,
38+
union=True,
39+
workable=False))
40+
assert sum(1 for s in strats if all(s.workable)) == 0
41+
assert sum(1 for s in strats if not any(s.workable)) == 14
42+
# Test union param with workable to True
43+
strats = list(general_factor(diverse_tiling,
44+
FactorWithInterleaving,
45+
union=True,
46+
workable=True))
47+
assert sum(1 for s in strats if all(s.workable)) == 1
48+
assert sum(1 for s in strats if not any(s.workable)) == 13
49+
50+
1151
def test_factor_no_unions(simple_tiling,
1252
diverse_tiling,
1353
no_point_tiling):
@@ -26,7 +66,7 @@ def test_factor_no_unions(simple_tiling,
2666
Tiling(obstructions=[Obstruction(Perm((0, 1)), [(0, 0), (0, 0)])])])
2767

2868
strats = [s.comb_classes
29-
for s in factor(diverse_tiling, interleaving=True)]
69+
for s in factor_with_interleaving(diverse_tiling)]
3070
assert len(strats) == 1
3171
factors = strats[0]
3272
assert len(factors) == 4
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
from .factor import factor
1+
from .factor import (factor, factor_with_interleaving,
2+
factor_with_monotone_interleaving, unions_of_factor,
3+
unions_of_factor_with_interleaving,
4+
unions_of_factor_with_monotone_interleaving)
Lines changed: 25 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,44 @@
11
from itertools import chain
22

33
from comb_spec_searcher import Rule
4-
from permuta.misc import UnionFind
5-
from tilings import Tiling
6-
from tilings.misc import union_reduce
4+
from tilings.algorithms import (Factor, FactorWithInterleaving,
5+
FactorWithMonotoneInterleaving)
76

87

9-
def factor(tiling, **kwargs):
8+
def general_factor(tiling, factor_class, union=False, **kwargs):
109
"""
11-
The factor strategy that decomposes a tiling into its connected factors.
12-
13-
The factors are the connected components of the graph of the tiling, where
14-
vertices are the cells. Two vertices are connected if there exists a
15-
obstruction or requirement occupying both cells. Two cells are also
16-
connected if they share the same row or column unless the interleaving or
17-
point_interleaving keyword arguments are set to True.
18-
When point interleavings are allowed, two cells in the same row or column
19-
are not connected. When general interleavings are allowed, two cells in the
20-
same row or column are not connected.
10+
Iterator of factor strategy.
2111
"""
22-
interleaving = kwargs.get("interleaving", False)
23-
point_interleaving = kwargs.get("point_interleaving", False)
24-
n, m = tiling.dimensions
25-
26-
def cell_to_int(cell):
27-
return cell[0] * m + cell[1]
28-
29-
def int_to_cell(i):
30-
return (i // m, i % m)
31-
32-
cells = list(tiling.active_cells)
33-
uf = UnionFind(n * m)
34-
35-
# Unite by obstructions
36-
for ob in tiling.obstructions:
37-
for i in range(len(ob.pos)):
38-
for j in range(i+1, len(ob.pos)):
39-
uf.unite(cell_to_int(ob.pos[i]), cell_to_int(ob.pos[j]))
40-
41-
# Unite by requirements
42-
for req_list in tiling.requirements:
43-
req_cells = list(union_reduce(req.pos for req in req_list))
44-
for i in range(len(req_cells)):
45-
for j in range(i + 1, len(req_cells)):
46-
uf.unite(cell_to_int(req_cells[i]), cell_to_int(req_cells[j]))
12+
assert factor_class in [Factor, FactorWithMonotoneInterleaving,
13+
FactorWithInterleaving]
14+
workable = kwargs.get('workable', True)
15+
factor = factor_class(tiling)
16+
if factor.factorable():
17+
yield factor.rule(workable=workable)
18+
if union:
19+
yield from factor.all_union_rules()
4720

48-
# If interleaving not allowed, unite by row/col
49-
if not interleaving:
50-
for i in range(len(cells)):
51-
for j in range(i+1, len(cells)):
52-
c1, c2 = cells[i], cells[j]
53-
if (point_interleaving and
54-
(c1 in tiling.point_cells or
55-
c2 in tiling.point_cells)):
56-
continue
57-
if c1[0] == c2[0] or c1[1] == c2[1]:
58-
uf.unite(cell_to_int(c1), cell_to_int(c2))
5921

60-
# Collect the connected components of the cells
61-
all_components = {}
62-
for cell in cells:
63-
i = uf.find(cell_to_int(cell))
64-
if i in all_components:
65-
all_components[i].append(cell)
66-
else:
67-
all_components[i] = [cell]
68-
component_cells = list(set(cells) for cells in all_components.values())
69-
70-
# If the tiling is a single connected component
71-
if len(component_cells) <= 1:
72-
return
73-
74-
# Collect the factors of the tiling
75-
factors = []
76-
strategy = [] # the vanilla factors
77-
for cell_component in component_cells:
78-
obstructions = [ob for ob in tiling.obstructions
79-
if ob.pos[0] in cell_component]
80-
requirements = [req for req in tiling.requirements
81-
if req[0].pos[0] in cell_component]
82-
83-
if obstructions or requirements:
84-
factors.append((obstructions, requirements))
85-
strategy.append(Tiling(obstructions=obstructions,
86-
requirements=requirements,
87-
minimize=False))
88-
89-
if kwargs.get("workable", True):
90-
work = [True for _ in strategy]
91-
else:
92-
work = [False for _ in strategy]
93-
94-
yield Rule("The factors of the tiling.", strategy,
95-
inferable=[False for _ in strategy], workable=work,
96-
possibly_empty=[False for _ in strategy],
97-
ignore_parent=kwargs.get("workable", True),
98-
constructor='cartesian')
22+
def factor(tiling, **kwargs):
23+
return general_factor(tiling, Factor, **kwargs)
9924

100-
if kwargs.get("unions", False):
101-
for partition in partition_list(factors):
102-
strategy = []
103-
for part in partition:
104-
obstructions, requirements = zip(*part)
105-
strategy.append(Tiling(obstructions=chain(*obstructions),
106-
requirements=chain(*requirements),
107-
minimize=False))
108-
yield Rule("The union of factors of the tiling",
109-
strategy,
110-
possibly_empty=[False for _ in strategy],
111-
inferable=[False for _ in strategy],
112-
workable=[False for _ in strategy],
113-
constructor='cartesian')
11425

26+
def factor_with_monotone_interleaving(tiling, **kwargs):
27+
return general_factor(tiling, FactorWithMonotoneInterleaving, **kwargs)
11528

116-
# The code below is magical and comes from
117-
# https://codereview.stackexchange.com/questions/1526/finding-all-k-subset-partitions
11829

30+
def factor_with_interleaving(tiling, **kwargs):
31+
return general_factor(tiling, FactorWithInterleaving, **kwargs)
11932

120-
def partition_list(lst):
121-
for i in range(2, len(lst)):
122-
for part in algorithm_u(lst, i):
123-
yield part
12433

34+
def unions_of_factor(tiling, **kwargs):
35+
return general_factor(tiling, Factor, union=True, **kwargs)
12536

126-
def algorithm_u(ns, m):
127-
def visit(n, a):
128-
ps = [[] for i in range(m)]
129-
for j in range(n):
130-
ps[a[j + 1]].append(ns[j])
131-
return ps
13237

133-
def f(mu, nu, sigma, n, a):
134-
if mu == 2:
135-
yield visit(n, a)
136-
else:
137-
for v in f(mu - 1, nu - 1, (mu + sigma) % 2, n, a):
138-
yield v
139-
if nu == mu + 1:
140-
a[mu] = mu - 1
141-
yield visit(n, a)
142-
while a[nu] > 0:
143-
a[nu] = a[nu] - 1
144-
yield visit(n, a)
145-
elif nu > mu + 1:
146-
if (mu + sigma) % 2 == 1:
147-
a[nu - 1] = mu - 1
148-
else:
149-
a[mu] = mu - 1
150-
if (a[nu] + sigma) % 2 == 1:
151-
for v in b(mu, nu - 1, 0, n, a):
152-
yield v
153-
else:
154-
for v in f(mu, nu - 1, 0, n, a):
155-
yield v
156-
while a[nu] > 0:
157-
a[nu] = a[nu] - 1
158-
if (a[nu] + sigma) % 2 == 1:
159-
for v in b(mu, nu - 1, 0, n, a):
160-
yield v
161-
else:
162-
for v in f(mu, nu - 1, 0, n, a):
163-
yield v
38+
def unions_of_factor_with_monotone_interleaving(tiling, **kwargs):
39+
return general_factor(tiling, FactorWithMonotoneInterleaving, union=True,
40+
**kwargs)
16441

165-
def b(mu, nu, sigma, n, a):
166-
if nu == mu + 1:
167-
while a[nu] < mu - 1:
168-
yield visit(n, a)
169-
a[nu] = a[nu] + 1
170-
yield visit(n, a)
171-
a[mu] = 0
172-
elif nu > mu + 1:
173-
if (a[nu] + sigma) % 2 == 1:
174-
for v in f(mu, nu - 1, 0, n, a):
175-
yield v
176-
else:
177-
for v in b(mu, nu - 1, 0, n, a):
178-
yield v
179-
while a[nu] < mu - 1:
180-
a[nu] = a[nu] + 1
181-
if (a[nu] + sigma) % 2 == 1:
182-
for v in f(mu, nu - 1, 0, n, a):
183-
yield v
184-
else:
185-
for v in b(mu, nu - 1, 0, n, a):
186-
yield v
187-
if (mu + sigma) % 2 == 1:
188-
a[nu - 1] = 0
189-
else:
190-
a[mu] = 0
191-
if mu == 2:
192-
yield visit(n, a)
193-
else:
194-
for v in b(mu - 1, nu - 1, (mu + sigma) % 2, n, a):
195-
yield v
19642

197-
n = len(ns)
198-
a = [0] * (n + 1)
199-
for j in range(1, m + 1):
200-
a[n - m + j] = j - 1
201-
return f(m, n, 0, n, a)
43+
def unions_of_factor_with_interleaving(tiling, **kwargs):
44+
return general_factor(tiling, FactorWithInterleaving, union=True, **kwargs)

0 commit comments

Comments
 (0)