def test_1234_fusion(): __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) with open(os.path.join(__location__, "spec-1234.json")) as f: d = json.loads(f.read()) spec = CombinatorialSpecification.from_dict(d) assert isinstance(spec, CombinatorialSpecification) assert not any("NOTIMPLEMENTED" in str(eq.rhs) for eq in spec.get_equations()) assert [spec.count_objects_of_size(i) for i in range(15)] == [ 1, 1, 2, 6, 23, 103, 513, 2761, 15767, 94359, 586590, 3763290, 24792705, 167078577, 1148208090, ] av = Av([Perm((0, 1, 2, 3))]) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i)) assert spec.random_sample_object_of_size(i).patt in av
def test_132_genf(): searcher = TileScope([Perm((0, 2, 1))], point_placements) spec = searcher.auto_search(smallest=True) spec = spec.expand_verified() av = Av([Perm((0, 2, 1))]) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i)) assert spec.random_sample_object_of_size(i).patt in av gf = spec.get_genf() gf = sympy.series(spec.get_genf(), n=15) x = sympy.Symbol("x") assert [gf.coeff(x, n) for n in range(13)] == [ 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, ]
def test_123_positive_fusions(): pack = TileScopePack.insertion_row_and_col_placements( row_only=True).make_fusion(tracked=True, apply_first=True) css = TileScope("123", pack) spec = css.auto_search(status_update=30) spec = spec.expand_verified() print(spec) assert isinstance(spec, CombinatorialSpecification) assert [spec.count_objects_of_size(i) for i in range(20)] == [ 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, ] av = Av([Perm((0, 1, 2))]) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i)) assert spec.random_sample_object_of_size(i).patt in av
def get_permclass(self): if self.is_empty(): return Av(Perm((0, ))) elif self.is_point(): return PermSet(1) elif self.is_anything(): return PermSet() elif self.is_avoiding() and not self.is_containing(): return Av(self.obstructions) else: return MockAvCoPatts(self.obstructions, self.requirements)
def __init__(self, av_patts, co_patts): self.base_perm_set = Av(av_patts) if isinstance(co_patts, (Perm, MeshPatt)): self.filter = lambda perm: perm.contains(co_patts) elif all(isinstance(patt, (Perm, MeshPatt)) for patt in co_patts): self.filter = lambda perm: any( perm.contains(patt) for patt in co_patts) else: raise ValueError("Variable 'co_patts' not as expected: " "'{}'".format(co_patts))
def verified(self, comb_class: Tiling) -> bool: if not comb_class.dimensions == (1, 1): return False if not self.basis: return True tiling_class = Av([ob.patt for ob in comb_class.obstructions]) sym_classes = (Av(sym) for sym in self.symmetries) is_strict_subclass = any( tiling_class.is_subclass(cls) and cls != tiling_class for cls in sym_classes ) return is_strict_subclass or any( isinstance(ass, ComponentAssumption) for ass in comb_class.assumptions )
def test_reverse_equiv(): """A specification that should use reverse equivalence.""" pack = TileScopePack( initial_strats=[ strat.FactorFactory(), strat.RequirementCorroborationFactory(), strat.RequirementPlacementFactory(partial=False), ], inferral_strats=[strat.RowColumnSeparationStrategy()], expansion_strats=[[strat.CellInsertionFactory()]], ver_strats=[strat.BasicVerificationStrategy()], iterative=False, name="test pack", ) basis = (Perm((0, 1, 3, 2)), Perm((0, 2, 3, 1)), Perm((1, 0, 3, 2))) # From https://oeis.org/A033321 expected_enum = [ 1, 1, 2, 6, 21, 79, 311, 1265, 5275, 22431, 96900, 424068, 1876143 ] x, f = sympy.symbols("x f") expected_min_poly = sympy.sympify("-4*f^2*x^2 + 8*f^2*x - 4*f*x - 4*f + 4") searcher = TileScope(basis, pack) spec = searcher.auto_search(smallest=True) assert [spec.count_objects_of_size(i) for i in range(13)] == expected_enum genf = spec.get_genf() assert sympy.simplify(expected_min_poly.subs(f, genf)) == 0 assert taylor_expand(genf, 12) == expected_enum # In order to avoid ReccursionError we go incrementally for i in range(0, 100): spec.count_objects_of_size(i) assert spec.count_objects_of_size(50) == 86055297645519796258217673160170 assert ( spec.count_objects_of_size(100) == 2733073112795720153237297124938915907723365837935699807314396095313) len4_perms = tuple(spec.generate_objects_of_size(4)) assert len(len4_perms) == 21 assert all(p not in len4_perms for p in basis) len8_perms = tuple(spec.generate_objects_of_size(8)) assert len(len8_perms) == 5275 assert len(set(len8_perms)) == 5275 for _ in range(10): gp = spec.random_sample_object_of_size(10) print(gp) assert gp.patt.avoids(*basis) av = Av(basis) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i)) assert spec.random_sample_object_of_size(i).patt in av
def __str__(self) -> str: if self.maxreqlen == 1: return "point insertion" if self.extra_basis: perm_class = Av(self.extra_basis) return f"cell insertion from {perm_class} up to length {self.maxreqlen}" return f"cell insertion up to length {self.maxreqlen}"
def __str__(self) -> str: if self.extra_basis: perm_class = Av(self.extra_basis) return "requirement extension from {} up to " "length {}".format( perm_class, self.maxreqlen) return "requirement extension insertion up to " "length {}".format( self.maxreqlen)
def req_lists_to_insert(self, tiling: Tiling) -> Iterator[ListRequirement]: active = tiling.active_cells bdict = tiling.cell_basis() for cell, length in product(active, range(1, self.maxreqlen + 1)): basis = bdict[cell][0] + self.extra_basis yield from ((GriddedPerm.single_cell(patt, cell), ) for patt in Av(basis).of_length(length) if not any(patt in perm for perm in bdict[cell][1]))
def __str__(self) -> str: if self.maxreqlen == 1: return "point insertion" if self.extra_basis: perm_class = Av(self.extra_basis) return "cell insertion from {} up to " "length {}".format( perm_class, self.maxreqlen) return "cell insertion up to length {}".format(self.maxreqlen)
def __str__(self) -> str: if not self.extra_basis: s = f"root insertion up to length {self.maxreqlen}" else: perm_class = Av(self.extra_basis) s = f"root insertion from {perm_class} up to length {self.maxreqlen}" if self.max_num_req is not None: s += f" (up to {self.max_num_req} requirements)" return s
def __str__(self) -> str: if not self.extra_basis: s = "root insertion up to length {}".format(self.maxreqlen) else: perm_class = Av(self.extra_basis) s = "root insertion from {} up to length {}".format( perm_class, self.maxreqlen) if self.max_num_req is not None: s += " (up to {} requirements)".format(self.max_num_req) return s
def req_lists_to_insert(self, tiling: Tiling) -> Iterator[ListRequirement]: bdict = tiling.cell_basis() cell_with_req = ((cell, obs, reqlist[0]) for cell, (obs, reqlist) in bdict.items() if len(reqlist) == 1) for cell, obs, curr_req in cell_with_req: for length in range(len(curr_req) + 1, self.maxreqlen + 1): for patt in Av(obs + self.extra_basis).of_length(length): if curr_req in patt: yield (GriddedPerm.single_cell(patt, cell), )
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. """ av = Av(basis) longest = 4 length = 4 while True: if length - 2 > longest: break for perm in av.of_length(length): if perm.is_simple(): yield perm longest = length length += 1
def test_get_elmnts_of_size_Av21_cell(self): mt = MeshTiling({ (0, 0): Cell(frozenset({Perm((1, 0))}), frozenset()), (1, 1): MeshTiling.point_cell }) for size in range(1, 5): expected_perms = set(Av([Perm((1, 0))]).of_length(size)) mt_perms = mt.get_elmnts(of_size=size) assert (len(set(mt_perms)) == len(list(mt_perms))) assert (set(mt_perms) == expected_perms)
def all_cell_insertions(tiling, **kwargs): """ The cell insertion strategy. The cell insertion strategy is a batch strategy that considers each active cells, excluding positive cells. For each of these cells, the strategy considers all patterns (up to some maximum length given by maxreqlen, and some maximum number given by maxreqnum) and returns two tilings; one which requires the pattern in the cell and one where the pattern is obstructed. TODO: - Have a flag to insert into positive cells that contain the maximal intersections """ maxreqlen = kwargs.get('maxreqlen') if not maxreqlen: maxreqlen = 1 ignore_parent = kwargs.get('ignore_parent', False) extra_basis = kwargs.get('extra_basis') if extra_basis is None: extra_basis = [] if (not isinstance(extra_basis, list) or not all(isinstance(p, Perm) for p in extra_basis)): raise TypeError("'extra_basis' flag should be a list of Perm to avoid") maxreqnum = kwargs.get('maxreqnum') if not maxreqnum: maxreqnum = 1 active = tiling.active_cells bdict = tiling.cell_basis() for cell in active: if len(bdict[cell][1]) >= maxreqnum: continue for length in range(1, maxreqlen + 1): for patt in Av(bdict[cell][0] + extra_basis).of_length(length): if not any(patt in perm for perm in bdict[cell][1]): if (tiling.dimensions != (1, 1) or all(patt > perm for perm in bdict[cell][1])): yield Rule( formal_step=("Insert {} into cell {}.|{}|{}|{}|" "".format( patt, cell, cell[0], cell[1], "".join(str(i) for i in patt))), comb_classes=cell_insertion(tiling, patt, cell), ignore_parent=ignore_parent, inferable=[True for _ in range(2)], possibly_empty=[True for _ in range(2)], workable=[True for _ in range(2)], constructor='disjoint')
class MockAvCoPatts: def __init__(self, av_patts, co_patts): self.base_perm_set = Av(av_patts) if isinstance(co_patts, (Perm, MeshPatt)): self.filter = lambda perm: perm.contains(co_patts) elif all(isinstance(patt, (Perm, MeshPatt)) for patt in co_patts): self.filter = lambda perm: any( perm.contains(patt) for patt in co_patts) else: raise ValueError("Variable 'co_patts' not as expected: " "'{}'".format(co_patts)) def of_length(self, size): return filter(self.filter, self.base_perm_set.of_length(size))
def req_lists_to_insert(self, tiling: Tiling) -> Iterator[ListRequirement]: if self.one_cell_only: assert self.maxreqlen == 1 and self.ignore_parent cells = sorted( frozenset(tiling.active_cells) - frozenset(tiling.positive_cells) ) if cells: yield (GriddedPerm.single_cell((0,), cells[0]),) return active = tiling.active_cells bdict = tiling.cell_basis() for cell, length in product(active, range(1, self.maxreqlen + 1)): basis = bdict[cell][0] + self.extra_basis patterns = Av(basis).of_length(length) if basis else Perm.of_length(length) yield from ( (GriddedPerm.single_cell(patt, cell),) for patt in patterns if not any(patt in perm for perm in bdict[cell][1]) )
def all_requirement_extensions(tiling, **kwargs): """Insert longer requirements in to cells which contain a requirement""" maxreqlen = kwargs.get('maxreqlen') if not maxreqlen: maxreqlen = 2 extra_basis = kwargs.get('extra_basis') if extra_basis is None: extra_basis = [] if (not isinstance(extra_basis, list) or not all(isinstance(p, Perm) for p in extra_basis)): raise TypeError("'extra_basis' flag should be a list of Perm to avoid") active = tiling.active_cells bdict = tiling.cell_basis() for cell in active: basis = bdict[cell][0] reqs = bdict[cell][1] if len(reqs) != 1: continue curr_req = reqs[0] for length in range(len(curr_req) + 1, maxreqlen + 1): for patt in Av(bdict[cell][0] + extra_basis).of_length(length): if curr_req in patt: yield Rule( formal_step=("Insert {} into cell {}." "".format(patt, cell)), comb_classes=[ tiling.add_single_cell_obstruction(patt, cell), tiling.add_single_cell_requirement(patt, cell) ], ignore_parent=False, possibly_empty=[ any(len(r) > 1 for r in tiling.requirements), True ], inferable=[True for _ in range(2)], workable=[True for _ in range(2)], constructor='disjoint')
def subclass_verified(tiling, basis, **kwargs): """The subclass verified strategy. A tiling is subclass verified if it only generates permutations in a proper subclass of Av(basis). """ if tiling.dimensions == (1, 1): return None else: maxlen = kwargs.get('maxpattlen', 4) patterns = set(perm for perm in chain( *[Av(basis).of_length(i) for i in range(maxlen + 1)])) maxlen += tiling.maximum_length_of_minimum_gridded_perm() for i in range(maxlen + 1): for g in tiling.objects_of_length(i): perm = g.patt patterns = set(pattern for pattern in patterns if perm.avoids(pattern)) if not patterns: return None return VerificationRule(formal_step=("The tiling belongs to the " "subclass obtained by adding" " the patterns {}." "".format(Basis(patterns))))
def test_321_1324(): searcher = TileScope("321_1324", reginsenc) spec = searcher.auto_search() assert isinstance(spec, CombinatorialSpecification) for i in range(20): gp = spec.random_sample_object_of_size(i) assert all(cell == (0, 0) for cell in gp.pos) assert gp.patt.avoids(Perm((2, 1, 0)), Perm((0, 2, 1, 3))) av = Av([Perm((2, 1, 0)), Perm((0, 2, 1, 3))]) for i in range(10): assert set(av.of_length(i)) == set( gp.patt for gp in spec.generate_objects_of_size(i)) assert [spec.count_objects_of_size(i) for i in range(50)] == [ 1, 1, 2, 5, 13, 32, 72, 148, 281, 499, 838, 1343, 2069, 3082, 4460, 6294, 8689, 11765, 15658, 20521, 26525, 33860, 42736, 53384, 66057, 81031, 98606, 119107, 142885, 170318, 201812, 237802, 278753, 325161, 377554, 436493, 502573, 576424, 658712, 750140, 851449, 963419, 1086870, 1222663, 1371701, 1534930, 1713340, 1907966, 2119889, 2350237, ]
def formal_step(self) -> str: cls = Av(min(self.symmetries)) if len(self.symmetries) == 1: return f"No cell is {cls}" return f"No cell is a symmetry of {cls}"