Beispiel #1
0
def deflation(tiling, **kwargs):
    """Yield all deflation strategies.
    TODO: Think about how this works with requirements."""
    if tiling.requirements:
        return
    bases = tiling.cell_basis()
    for cell in tiling.possibly_empty:
        cell_basis = bases[cell][0]
        if sum_closed(cell_basis):
            if can_deflate(tiling, cell, True):
                yield Rule("Sum deflate cell {}.".format(cell),
                           [deflated_tiling(tiling, cell, sum_decomp=True)],
                           inferable=[True],
                           workable=[True],
                           possibly_empty=[False],
                           ignore_parent=False,
                           constructor="other")
        if skew_closed(cell_basis):
            if can_deflate(tiling, cell, False):
                yield Rule("Skew deflate cell {}.".format(cell),
                           [deflated_tiling(tiling, cell, sum_decomp=False)],
                           inferable=[True],
                           workable=[True],
                           possibly_empty=[False],
                           ignore_parent=False,
                           constructor="other")
Beispiel #2
0
def requirement_corroboration(tiling, basis, **kwargs):
    """
    The requirement corroboration strategy.

    The requirement corrobation strategy is a batch strategy that considers
    each requirement of each requirement list. For each of these requirements,
    the strategy returns two tilings; one where the requirement has been turned
    into an obstruction and another where the requirement has been singled out
    and a new requirement list added with only the requirement. This new
    requirement list contains only the singled out requirement.

    This implements the notion of partitioning the set of gridded permutations
    into those that satisfy this requirement and those that avoid it. Those
    that avoid the requirement, must therefore satisfy another requirement from
    the same list and hence the requirement list must be of length at least 2.
    """
    for reqs in tiling.requirements:
        if len(reqs) == 1:
            continue
        for req in reqs:
            yield Rule(formal_step="Inserting requirement {}.".format(
                str(req)),
                       comb_classes=gp_insertion(tiling, req),
                       ignore_parent=True,
                       possibly_empty=[True, True],
                       workable=[True for _ in range(2)],
                       inferable=[True for _ in range(2)],
                       constructor='disjoint')
Beispiel #3
0
def all_requirement_insertions(tiling, **kwargs):
    """Insert all possible requirements the obstruction allow."""
    if kwargs.get("no_reqs", True) and tiling.requirements:
        return
    maxlen = kwargs.get("maxlen", 2)
    ignore_parent = kwargs.get("ignore_parent", False)
    obs_tiling = Tiling(tiling.obstructions,
                        remove_empty=False,
                        derive_empty=False,
                        minimize=False,
                        sorted_input=True)
    for length in range(1, maxlen + 1):
        for gp in obs_tiling.gridded_perms_of_length(length):
            if len(gp.factors()) == 1:
                av = Tiling(
                    (tiling.obstructions + (Obstruction(gp.patt, gp.pos), )),
                    tiling.requirements)
                co = Tiling(tiling.obstructions, (tiling.requirements) +
                            ((Requirement(gp.patt, gp.pos), ), ))
                yield Rule(formal_step="Insert {}.".format(str(gp)),
                           comb_classes=[av, co],
                           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')
Beispiel #4
0
def requirement_placement(tiling, **kwargs):
    """
    Strategy that places a single forced point of a requirement.

    The requirement_placement strategy considers every requirement list of
    length exactly 1. For each of these requirements, it considers all the
    points of the requirement. The strategy then returns all tilings where the
    point has been placed with a force.
    """
    point_cells = tiling.point_cells
    point_only = kwargs.get('point_only')
    ignore_parent = kwargs.get('ignore_parent', False)
    for ri, reqs in enumerate(tiling.requirements):
        if len(reqs) > 1:
            continue
        if (reqs[0].is_point_perm() in point_cells
                and tiling.only_cell_in_row_and_col(reqs[0].is_point_perm())):
            continue
        if point_only and reqs[0].is_point_perm() is None:
            continue
        for i in range(len(reqs[0])):
            for DIR in DIRS:
                placedtiling = place_point_of_requirement(tiling, ri, i, DIR)
                yield Rule(formal_step=("Placing point {} of requirement {} "
                                        "with force {}").format(
                                            (i, reqs[0].patt[i]), str(reqs[0]),
                                            DIR),
                           comb_classes=[placedtiling],
                           ignore_parent=ignore_parent,
                           inferable=[True],
                           possibly_empty=[True],
                           workable=[True],
                           constructor='equiv')
Beispiel #5
0
def equivalent(av_config, **kwargs):
    """Yield equivalence rules of the configurations that are the same by
    some point being removed."""
    if av_config.last_letter is None:
        for config in av_config.equivalent_configs():
            yield Rule("The configurations are equivalent",
                       [Suffixes(config, av_config.basis)], [True], [False],
                       [True],
                       ignore_parent=True,
                       constructor='equiv')
Beispiel #6
0
def fusion(tiling, **kwargs):
    """Yield rules found by fusing rows and columns of a tiling."""
    for row_index in range(tiling.dimensions[1] - 1):
        if fusable(tiling, row_index, True):
            yield Rule(("Fuse rows {} and {}|{}|."
                        "").format(row_index, row_index + 1, row_index),
                       [fuse_tiling(tiling, row_index, True)],
                       inferable=[True],
                       workable=[True],
                       possibly_empty=[False],
                       constructor='other')
    for col_index in range(tiling.dimensions[0] - 1):
        if fusable(tiling, col_index, False):
            yield Rule(("Fuse columns {} and {}|{}|."
                        "").format(col_index, col_index + 1, col_index),
                       [fuse_tiling(tiling, col_index, False)],
                       inferable=[True],
                       workable=[True],
                       possibly_empty=[False],
                       constructor='other')
Beispiel #7
0
def targeted_cell_insertion(tiling, **kwargs):
    """Return combintorial rules formed by inserting """
    factors = factors_of_gridded_perm(tiling)
    if not factors:
        return
    for f in factors:
        yield Rule("Insert {}.".format(repr(f)), [
            tiling.add_obstruction(f.patt, f.pos),
            tiling.add_requirement(f.patt, f.pos)
        ],
                   inferable=[True, True],
                   workable=[True, True],
                   ignore_parent=True,
                   constructor='disjoint')
Beispiel #8
0
def all_col_insertions(tiling, **kwargs):
    """Insert a list requirement into every possibly empty column."""
    positive_cells = tiling.positive_cells
    for col in range(tiling.dimensions[0]):
        col_cells = tiling.cells_in_col(col)
        if any(c in positive_cells for c in col_cells):
            continue
        yield Rule(formal_step="Either col {} is empty or not.".format(col),
                   comb_classes=col_insertion_helper(tiling, col, col_cells),
                   ignore_parent=False,
                   inferable=[True for _ in range(2)],
                   possibly_empty=[True, True],
                   workable=[True for _ in range(2)],
                   constructor='disjoint')
Beispiel #9
0
def all_row_insertions(tiling, **kwargs):
    """Insert a list requirement into every possibly empty row."""
    positive_cells = tiling.positive_cells
    for row in range(tiling.dimensions[1]):
        row_cells = tiling.cells_in_row(row)
        if any(c in positive_cells for c in row_cells):
            continue
        yield Rule(formal_step="Either row {} is empty or not.".format(row),
                   comb_classes=row_insertion_helper(tiling, row, row_cells),
                   ignore_parent=False,
                   inferable=[True for _ in range(2)],
                   possibly_empty=[True, True],
                   workable=[True for _ in range(2)],
                   constructor='disjoint')
Beispiel #10
0
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')
Beispiel #11
0
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')
Beispiel #12
0
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
Beispiel #13
0
def all_factor_insertions(tiling, **kwargs):
    ignore_parent = kwargs.get("ignore_parent", False)
    for gp in sorted(set(chain(tiling.obstructions, *tiling.requirements))):
        factors = gp.factors()
        if len(factors) != 1:
            for gp in factors:
                av = Tiling(
                    (tiling.obstructions + (Obstruction(gp.patt, gp.pos), )),
                    tiling.requirements)
                co = Tiling(tiling.obstructions, (tiling.requirements) +
                            ((Requirement(gp.patt, gp.pos), ), ))
                yield Rule(formal_step="Insert {}.".format(str(gp)),
                           comb_classes=[av, co],
                           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')
Beispiel #14
0
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
Beispiel #15
0
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')
Beispiel #16
0
def factor(tiling, **kwargs):
    """
    The factor strategy that decomposes a tiling into its connected factors.

    The factors are the connected components of the graph of the tiling, where
    vertices are the cells. Two vertices are connected if there exists a
    obstruction or requirement occupying both cells. Two cells are also
    connected if they share the same row or column unless the interleaving or
    point_interleaving keyword arguments are set to True.
    When point interleavings are allowed, two cells in the same row or column
    are not connected. When general interleavings are allowed, two cells in the
    same row or column are not connected.
    """
    interleaving = kwargs.get("interleaving", False)
    point_interleaving = kwargs.get("point_interleaving", False)
    n, m = tiling.dimensions

    def cell_to_int(cell):
        return cell[0] * m + cell[1]

    def int_to_cell(i):
        return (i // m, i % m)

    cells = list(tiling.active_cells)
    uf = UnionFind(n * m)

    # Unite by obstructions
    for ob in tiling.obstructions:
        for i in range(len(ob.pos)):
            for j in range(i+1, len(ob.pos)):
                uf.unite(cell_to_int(ob.pos[i]), cell_to_int(ob.pos[j]))

    # Unite by requirements
    for req_list in tiling.requirements:
        req_cells = list(union_reduce(req.pos for req in req_list))
        for i in range(len(req_cells)):
            for j in range(i + 1, len(req_cells)):
                uf.unite(cell_to_int(req_cells[i]), cell_to_int(req_cells[j]))

    # If interleaving not allowed, unite by row/col
    if not interleaving:
        for i in range(len(cells)):
            for j in range(i+1, len(cells)):
                c1, c2 = cells[i], cells[j]
                if (point_interleaving and
                        (c1 in tiling.point_cells or
                         c2 in tiling.point_cells)):
                    continue
                if c1[0] == c2[0] or c1[1] == c2[1]:
                    uf.unite(cell_to_int(c1), cell_to_int(c2))

    # Collect the connected components of the cells
    all_components = {}
    for cell in cells:
        i = uf.find(cell_to_int(cell))
        if i in all_components:
            all_components[i].append(cell)
        else:
            all_components[i] = [cell]
    component_cells = list(set(cells) for cells in all_components.values())

    # If the tiling is a single connected component
    if len(component_cells) <= 1:
        return

    # Collect the factors of the tiling
    factors = []
    strategy = []  # the vanilla factors
    for cell_component in component_cells:
        obstructions = [ob for ob in tiling.obstructions
                        if ob.pos[0] in cell_component]
        requirements = [req for req in tiling.requirements
                        if req[0].pos[0] in cell_component]

        if obstructions or requirements:
            factors.append((obstructions, requirements))
            strategy.append(Tiling(obstructions=obstructions,
                                   requirements=requirements,
                                   minimize=False))

    if kwargs.get("workable", True):
        work = [True for _ in strategy]
    else:
        work = [False for _ in strategy]

    yield Rule("The factors of the tiling.", strategy,
               inferable=[False for _ in strategy], workable=work,
               possibly_empty=[False for _ in strategy],
               ignore_parent=kwargs.get("workable", True),
               constructor='cartesian')

    if kwargs.get("unions", False):
        for partition in partition_list(factors):
            strategy = []
            for part in partition:
                obstructions, requirements = zip(*part)
                strategy.append(Tiling(obstructions=chain(*obstructions),
                                       requirements=chain(*requirements),
                                       minimize=False))
            yield Rule("The union of factors of the tiling",
                       strategy,
                       possibly_empty=[False for _ in strategy],
                       inferable=[False for _ in strategy],
                       workable=[False for _ in strategy],
                       constructor='cartesian')
Beispiel #17
0
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]

    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.")

    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')