-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsubdecomp.py
378 lines (307 loc) · 14 KB
/
subdecomp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
"""
A demo file for Permutation Pattern 2019 pre-conference workshop for the
computational mathematics line on automatic methods in enumerating permutation
classes, given by Christian Bean. This demo allows you to implement your own
version of an algorithm for performing the substitution decompostion to classes
with finitely many simple permutatios. It uses the permuta, tilings, and
comb_spec_searcher modules from the PermutaTriangle.
https://github.com/PermutaTriangle
You will need to implement the following functions:
- embeddings
- simples_avoiding
- expansion
- factor
"""
from itertools import chain
from sympy import var
from sympy.abc import x
from comb_spec_searcher import (BatchRule, CombinatorialClass,
CombinatorialSpecificationSearcher,
DecompositionRule, Rule, StrategyPack,
VerificationRule)
from permuta import Av, Perm
from tilings import Obstruction, Requirement, Tiling
def embeddings(perm, cells):
"""
Yield all possible embeddings of perm into the given cells.
An embedding should be a tuple (or iterable) of cells.
"""
pass
def simples_avoiding(basis):
"""
Yield all simple permutations that avoid basis.
You may wish to use the 'Av' class from permuta for iterating over
permutations that avoid a given basis.
A naive approach to know when to terminate is to use the fact that every
length n simple contains a simple of length n - 1 or length n - 2.
"""
pass
def stretch_perms(perms, cells, obstruction=True):
"""Return all gridded perms with underlying perm in perms using cells."""
if obstruction:
GP = Obstruction
else:
GP = Requirement
res = []
for perm in perms:
for embedding in embeddings(perm, cells):
res.append(GP(perm, embedding))
return res
def stretch_av_and_co(av, co, cells):
"""
Return the obstruction and requirement implied by stretching all avoided
and contained patterns.
This will return a pair (O, R) where O is a list of gridded perms (of type
Obstruction) and R is a list of lists of gridded perms (of type
requirement).
"""
return (stretch_perms(av, cells),
[stretch_perms(c, cells, False) for c in co])
class SubDecomp(CombinatorialClass):
"""
This is the base class for using the CombinatorialSpecificationSearcher.
"""
def __init__(self):
self.tiling = None
def is_empty(self):
return self.tiling.is_empty()
def get_avoids_contains(self):
if self.tiling.dimensions == (1, 1):
return ([ob.patt for ob in self.tiling.obstructions],
[[r.patt for r in rq] for rq in self.tiling.requirements])
def get_genf(self, **kwargs):
"""If it is the point tiling then return x else None."""
if (self.tiling.is_point_tiling() and
(isinstance(self, Point) or
isinstance(self, SumIndecomposable) or
isinstance(self, SkewDecomposable) or
isinstance(self, All))):
return x
def get_min_poly(self, **kwargs):
"""If it is the point tiling then return x else None."""
if (self.tiling.is_point_tiling() and
(isinstance(self, Point) or
isinstance(self, SumIndecomposable) or
isinstance(self, SkewDecomposable) or
isinstance(self, All))):
return var('F') - x
def objects_of_length(self, length):
for gp in self.tiling.objects_of_length(length):
yield gp.patt
def to_jsonable(self):
return self.tiling.to_jsonable()
def __eq__(self, other):
return type(self) == type(other) and self.tiling == other.tiling
def __hash__(self):
return hash(self.tiling)
def __repr__(self):
return repr(self.tiling)
def __str__(self):
res = self.__class__.__name__ + "\n"
res += str(self.tiling)
return res
class Point(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple()):
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 0)])
obs.extend([Obstruction.single_cell(Perm((0, 1)), (0, 0)),
Obstruction.single_cell(Perm((1, 0)), (0, 0))])
reqs.extend([[Requirement.single_cell(Perm((0,)), (0, 0))]])
self.tiling = Tiling(obs, reqs)
class All(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple()):
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 0)])
reqs.append([Requirement.single_cell(Perm((0,)), (0, 0))])
self.tiling = Tiling(obs, reqs)
class SumDecomposable(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple(), tiling=None):
if tiling is not None and isinstance(tiling, Tiling):
self.tiling = tiling
else:
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 0), (1, 1)])
reqs.extend([[Requirement.single_cell(Perm((0,)), (0, 0))],
[Requirement.single_cell(Perm((0,)), (1, 1))]])
self.tiling = Tiling(obs, reqs)
def is_empty(self):
if any(ob.is_empty() for ob in self.tiling.obstructions):
return True
mxln = max(self.tiling.maximum_length_of_minimum_gridded_perm() + 4, 1)
return all(
gp.get_gridded_perm_in_cells([(0, 0)]).patt.is_sum_decomposable()
for gp in self.tiling.gridded_perms(maxlen=mxln))
@classmethod
def from_tiling(cls, tiling):
return SumDecomposable(tiling=tiling)
def objects_of_length(self, length):
for obj in self.tiling.objects_of_length(length):
subob = obj.get_gridded_perm_in_cells([(0, 0)])
if not subob.patt.is_sum_decomposable():
yield obj.patt
class SkewDecomposable(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple(), tiling=None):
if tiling is not None and isinstance(tiling, Tiling):
self.tiling = tiling
else:
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 1), (1, 0)])
reqs.extend([[Requirement.single_cell(Perm((0,)), (0, 1))],
[Requirement.single_cell(Perm((0,)), (1, 0))]])
self.tiling = Tiling(obs, reqs)
def is_empty(self):
if any(ob.is_empty() for ob in self.tiling.obstructions):
return True
mxln = max(self.tiling.maximum_length_of_minimum_gridded_perm() + 4, 1)
return all(
gp.get_gridded_perm_in_cells([(0, 1)]).patt.is_skew_decomposable()
for gp in self.tiling.gridded_perms(maxlen=mxln))
@classmethod
def from_tiling(cls, tiling):
return SkewDecomposable(tiling=tiling)
def objects_of_length(self, length):
for obj in self.tiling.objects_of_length(length):
subob = obj.get_gridded_perm_in_cells([(0, 1)])
if not subob.patt.is_skew_decomposable():
yield obj.patt
class SimpleInflation(SubDecomp):
def __init__(self, perm=Perm(), avoids=tuple(), contains=tuple(),
tiling=None):
if tiling is not None and isinstance(tiling, Tiling):
self.tiling = tiling
else:
if not isinstance(perm, Perm) or not perm.is_simple():
raise ValueError("The first argument must be a simple Perm.")
cells = [(i, v) for i, v in enumerate(perm)]
obs, reqs = stretch_av_and_co(avoids, contains, cells)
reqs.extend([[Requirement.single_cell(Perm((0,)), cell)]
for cell in cells])
self.tiling = Tiling(obs, reqs)
@classmethod
def from_tiling(cls, tiling):
return SimpleInflation(tiling=tiling)
class SumIndecomposable(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple()):
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 0)])
reqs.append([Requirement.single_cell(Perm((0,)), (0, 0))])
self.tiling = Tiling(obs, reqs)
def objects_of_length(self, length):
for obj in self.tiling.objects_of_length(length):
if not obj.patt.is_sum_decomposable():
yield obj.patt
def is_empty(self):
if any(ob.is_empty() for ob in self.tiling.obstructions):
return True
mxln = max(self.tiling.maximum_length_of_minimum_gridded_perm() + 4, 1)
return all(gp.is_sum_decomposable()
for gp in self.tiling.gridded_perms(maxlen=mxln))
class SkewIndecomposable(SubDecomp):
def __init__(self, avoids=tuple(), contains=tuple()):
obs, reqs = stretch_av_and_co(avoids, contains, [(0, 0)])
reqs.append([Requirement.single_cell(Perm((0,)), (0, 0))])
self.tiling = Tiling(obs, reqs)
def objects_of_length(self, length):
for obj in self.tiling.objects_of_length(length):
if not obj.patt.is_skew_decomposable():
yield obj.patt
def is_empty(self):
if any(ob.is_empty() for ob in self.tiling.obstructions):
return True
mxln = max(self.tiling.maximum_length_of_minimum_gridded_perm() + 4, 1)
return all(gp.is_skew_decomposable()
for gp in self.tiling.gridded_perms(maxlen=mxln))
def expansion(tiling, **kwargs):
if isinstance(tiling, All):
comb_classes = []
# TODO: perform the substitution decomposition
# You may wish to use the get_avoids_contains method.
if comb_classes:
yield BatchRule("Appropriate description", comb_classes)
elif isinstance(tiling, SumIndecomposable):
comb_classes = []
# TODO: perform the substitution decomposition.
# You may wish to use the get_avoids_contains method.
if comb_classes:
yield BatchRule("Appropriate description", comb_classes)
elif isinstance(tiling, SkewIndecomposable):
comb_classes = []
# TODO: perform the substitution decomposition.
# You may wish to use the get_avoids_contains method.
if comb_classes:
yield BatchRule("Appropriate description", comb_classes)
def factor(tiling, **kwargs):
if (isinstance(tiling, SumDecomposable) or
isinstance(tiling, SkewDecomposable) or
isinstance(tiling, SimpleInflation)):
if (all(gp.is_single_cell()
for gp in chain(tiling.tiling.obstructions,
*tiling.tiling.requirements)) and
all(len(req_list) == 1
for req_list in tiling.tiling.requirements)):
comb_classes = []
for cell, (av, co) in tiling.tiling.cell_basis().items():
if len(av) == 1 and len(av[0]) == 1:
raise ValueError("This shouldn't happen")
co = [[p] for p in co]
# TODO: perform the substitution decomposition, you will need
# to consider the assumptions made depending on whether this is
# SumDecomposable, SkewDecomposable, or a SimpleInflation
if comb_classes:
yield DecompositionRule("Factor", comb_classes)
def all_factor_insertions(tiling, **kwargs):
if (isinstance(tiling, SumDecomposable) or
isinstance(tiling, SkewDecomposable) or
isinstance(tiling, SimpleInflation)):
for gp in sorted(set(chain(tiling.tiling.obstructions,
*tiling.tiling.requirements))):
factors = gp.factors()
if len(factors) != 1:
for gp in factors:
av = tiling.tiling.add_obstruction(gp.patt, gp.pos)
co = tiling.tiling.add_requirement(gp.patt, gp.pos)
comb_classes = [tiling.__class__.from_tiling(av),
tiling.__class__.from_tiling(co)]
yield Rule(formal_step="Insert {}.".format(str(gp)),
comb_classes=comb_classes,
ignore_parent=True,
inferable=[True for _ in range(2)],
possibly_empty=[True for _ in range(2)],
workable=[True for _ in range(2)],
constructor='disjoint')
break
break
def list_cleanup(tiling, **kwargs):
for gp in chain(*[req_list
for req_list in tiling.tiling.requirements
if len(req_list) > 1]):
av = tiling.tiling.add_obstruction(gp.patt, gp.pos)
co = tiling.tiling.add_requirement(gp.patt, gp.pos)
comb_classes = [tiling.__class__.from_tiling(av),
tiling.__class__.from_tiling(co)]
yield Rule(formal_step="Insert {}.".format(str(gp)),
comb_classes=comb_classes,
ignore_parent=True,
inferable=[True for _ in range(2)],
possibly_empty=[True for _ in range(2)],
workable=[True for _ in range(2)],
constructor='disjoint')
break
def verify_point(tiling, **kwargs):
"""Verify exactly the point tiling."""
if (tiling.tiling.is_point_tiling()):
if (isinstance(tiling, Point) or
isinstance(tiling, SumIndecomposable) or
isinstance(tiling, SkewDecomposable) or
isinstance(tiling, All)):
return VerificationRule("its the point")
pack = StrategyPack(initial_strats=[factor, list_cleanup,
all_factor_insertions],
inferral_strats=[],
expansion_strats=[[expansion]],
ver_strats=[verify_point],
name=("substition_decomposition"))
if __name__ == "__main__":
import sys
basis = sys.argv[1]
basis = [Perm.to_standard(p) for p in basis.split('_')]
start_class = All(basis)
searcher = CombinatorialSpecificationSearcher(start_class, pack,
debug=True)
tree = searcher.auto_search(status_update=30)
tree.get_min_poly(solve=True)