def deflated_tiling(tiling, cell, sum_decomp=True): """Return tiling where cell is deflated.""" if sum_decomp: extra = Obstruction.single_cell(Perm((1, 0)), cell) else: extra = Obstruction.single_cell(Perm((0, 1)), cell) return Tiling(requirements=tiling.requirements, obstructions=tiling.obstructions + (extra, ))
def __init__(self, n=None, k=None, strategy_pack=None, flogger_kwargs={'processname': 'runner'},**kwargs): self.start_tilings = [] if filename is not None: assert n == None and k == None f = open(filename, 'r') for line in f: line = line.strip() self.start_tilings.append(Tiling.from_string(line)) f.close() else: for basis in combinations(PermSet(n), k): self.start_tilings.append(Tiling([Obstruction.single_cell(patt, (0, 0)) for patt in basis])) strategy_pack.ver_strats = [verify_points] function_kwargs = {"basis": []} function_kwargs.update(kwargs.get('kwargs', dict())) CombinatorialSpecificationSearcher.__init__( self, self.start_tilings[0], strategy_pack, function_kwargs=function_kwargs, **kwargs) self.start_labels = [] for start_tiling in self.start_tilings: self.classdb.add(start_tiling, expandable=True) self.start_labels.append(self.classdb.get_label(start_tiling)) for label in self.start_labels: self.classqueue.add_to_working(label)
def twist_one_by_ones(tiling): """ Returns all tilings which can reached by twisting a single cell. """ if tiling.requirements: raise NotImplementedError("Can't handle requirements") one_by_ones = set(tiling.active_cells) for ob in tiling.obstructions: if not one_by_ones: break if not ob.is_single_cell(): for c in ob.pos: one_by_ones.discard(c) sym_sets = [ antidiagonal_set, complement_set, inverse_set, rotate_90_clockwise_set, rotate_180_clockwise_set, rotate_270_clockwise_set ] cell_basis = tiling.cell_basis() twists = set() for cell in one_by_ones: av, _ = cell_basis[cell] for sym_set in sym_sets: sym_av = sym_set(av) twisted_obs = ( [ob for ob in tiling.obstructions if cell not in ob.pos] + [Obstruction.single_cell(p, cell) for p in sym_av]) twists.add(Tiling(twisted_obs)) return set(twists)
def can_deflate(tiling, cell, sum_decomp): alone_in_row = tiling.only_cell_in_row(cell) alone_in_col = tiling.only_cell_in_col(cell) if alone_in_row and alone_in_col: return False deflate_patt = Obstruction.single_cell( Perm((1, 0)) if sum_decomp else Perm((0, 1)), cell) # we must be sure that no cell in a row or column can interleave # with any reinflated components, so collect cells that do not. cells_not_interleaving = set([cell]) for ob in tiling.obstructions: if ob == deflate_patt: return False if ob.is_single_cell() or not ob.occupies(cell): continue number_points_in_cell = sum(1 for c in ob.pos if c == cell) if number_points_in_cell == 1: if len(ob) == 2: # not interleaving with cell as separating if # in same row or column other_cell = [c for c in ob.pos if c != cell][0] cells_not_interleaving.add(other_cell) elif number_points_in_cell == 2: if len(ob) != 3: return False patt_in_cell = ob.get_gridded_perm_in_cells((cell, )) if patt_in_cell != deflate_patt: # you can interleave with components return False # we need the other cell to be in between the intended deflate # patt in either the row or column other_cell = [c for c in ob.pos if c != cell][0] if (point_in_between(ob, True, cell, other_cell) or point_in_between(ob, False, cell, other_cell)): # this cell does not interleave with inflated components cells_not_interleaving.add(other_cell) else: return False elif number_points_in_cell >= 3: # you can interleave with components return False # check that do not interleave with any cells in row or column. return (cells_not_interleaving >= tiling.cells_in_row(cell[1]) and cells_not_interleaving >= tiling.cells_in_col(cell[0]))
def __init__(self, start_class, strategy_pack, # symmetry=False, forward_equivalence=False, logger_kwargs={'processname': 'runner'}, **kwargs): """Initialise TileScope.""" if isinstance(start_class, str): basis = Basis([Perm.to_standard([int(c) for c in p]) for p in start_class.split('_')]) elif isinstance(start_class, list): basis = Basis(start_class) elif isinstance(start_class, Tiling): start_tiling = start_class if start_class.dimensions == (1, 1): basis = Basis([o.patt for o in start_class.obstructions]) else: basis = [] if not isinstance(start_class, Tiling): start_tiling = Tiling( obstructions=[Obstruction.single_cell(patt, (0, 0)) for patt in basis]) if strategy_pack.symmetries==True: symmetries = [Tiling.inverse, Tiling.reverse, Tiling.complement, Tiling.antidiagonal, Tiling.rotate90, Tiling.rotate180, Tiling.rotate270] # symmetries = [sym for sym in symmetries # if sym(start_tiling) == start_tiling] strategy_pack.symmetries = symmetries else: symmetries = [] function_kwargs = {"basis": basis} function_kwargs.update(kwargs.get('kwargs', dict())) CombinatorialSpecificationSearcher.__init__( self, start_tiling, strategy_pack, symmetry=symmetries, forward_equivalence=forward_equivalence, function_kwargs=function_kwargs, logger_kwargs=logger_kwargs, **kwargs)
def col_insertion_helper(tiling, col, col_cells, regions=False): if col_cells is None: col_cells = tiling.cells_in_col(col) col_req = tuple(Requirement.single_cell(Perm((0, )), c) for c in col_cells) col_obs = tuple(Obstruction.single_cell(Perm((0, )), c) for c in col_cells) if regions: return ([ Tiling(tiling.obstructions + col_obs, tiling.requirements), Tiling(tiling.obstructions, tiling.requirements + (col_req, )) ], [{c: frozenset([c]) for c in tiling.active_cells}, {c: frozenset([c]) for c in tiling.active_cells}]) else: return [ Tiling(tiling.obstructions + col_obs, tiling.requirements), Tiling(tiling.obstructions, tiling.requirements + (col_req, )) ]
def row_insertion_helper(tiling, row, row_cells, regions=False): if row_cells is None: row_cells = tiling.cells_in_row(row) row_req = tuple(Requirement.single_cell(Perm((0, )), c) for c in row_cells) row_obs = tuple(Obstruction.single_cell(Perm((0, )), c) for c in row_cells) if regions: return ([ Tiling(tiling.obstructions + row_obs, tiling.requirements), Tiling(tiling.obstructions, tiling.requirements + (row_req, )) ], [{c: frozenset([c]) for c in tiling.active_cells}, {c: frozenset([c]) for c in tiling.active_cells}]) else: return [ Tiling(tiling.obstructions + row_obs, tiling.requirements), Tiling(tiling.obstructions, tiling.requirements + (row_req, )) ]
def empty_cell_inferral(tiling, **kwargs): """The empty cell inferral strategy. The strategy considers each active but non-positive cell and inserts a point requirement. If the resulting tiling is empty, then a point obstruction can be added into the cell, i.e. the cell is empty.""" active = set(tiling.active_cells) positive = set(tiling.positive_cells) empty_cells = [] for cell in active - positive: reqtil = tiling.insert_cell(cell) if reqtil.is_empty(): empty_cells.append(cell) newobs = [Obstruction.single_cell(Perm((0,)), cell) for cell in empty_cells] return InferralRule( "The cells {} are empty".format(", ".join(map(str, empty_cells))), Tiling(obstructions=tiling.obstructions + tuple(newobs), requirements=tiling.requirements))
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)
def place_requirement_list(tiling, req_list, direction, regions=False): """Return the list of tilings obtained by placing the direction-most point of a requirement list. This represents a batch strategy, where the direction-most point of each requirement in the list is placed.""" # Compute the points furthest in the given direction. min_points = minimum_points(req_list, direction) if len([c for _, c in min_points]) != len(set([c for _, c in min_points])): # Can't handle list requirements with more than req farthest in the # direction in same cell. return None # For each tiling, compute the tiling where this point is placed furthest # in that direction. res = [] if regions: forward_maps = [] for (idx, cell), req in zip(min_points, req_list): # Placing the forced occurrence of the point in the requirement new_req, forced_obstructions = req.place_forced_point(idx, direction) assert len(new_req) == 1 # Add the forced obstruction to ensure no other requirement has a point # further in that direction. forced_obstructions = (forced_obstructions + list( chain.from_iterable( r.other_req_forced_point(cell, direction) for r in req_list if r != req))) # New indices of the point point_cell = (cell[0] + 1, cell[1] + 1) # The set of new obstructions, consisting of the forced obstructions, # other obstructions where the point placement has been taken into # account and the 12, 21 in the cell. newobs = forced_obstructions + list( chain.from_iterable( ob.place_point(cell, DIR_NONE) for ob in tiling.obstructions)) + [ Obstruction.single_cell(Perm((0, 1)), point_cell), Obstruction.single_cell(Perm((1, 0)), point_cell) ] # The rest of the requirements other_reqs = [reqs for reqs in tiling.requirements if reqs != req_list] # The new requirements, consisting of the requirement with the point # placed, other requirements where point placement has been taken into # account and the point requirement in the cell. newreqs = [ list( chain.from_iterable( r.place_point(cell, DIR_NONE) for r in reqs)) for reqs in other_reqs ] + [new_req] + [[Requirement.single_cell(Perm((0, )), point_cell)]] placed_tiling = Tiling(newobs, newreqs) res.append(placed_tiling) if regions: def cell_map(c): mindex, minval = c maxdex = mindex + 1 maxval = minval + 1 if mindex >= cell[0]: maxdex += 2 if minval >= cell[1]: maxval += 2 if mindex > cell[0]: mindex += 2 if minval > cell[1]: minval += 2 return frozenset([(x, y) for x in range(mindex, maxdex) for y in range(minval, maxval)]) forward_maps.append({c: cell_map(c) for c in tiling.active_cells}) if regions: return res, forward_maps else: return res
def partial_place_point_of_requirement(tiling, req_index, point_index, force_dir): """ Places the point at point_index in requirement at req_index into tiling on its own row or onto its own column depending on force_dir. """ if len(tiling.requirements[req_index]) > 1: raise ValueError( "Requirement list at {} contains more than 1 requirement.".format( req_index)) # Determine if placing onto own row or column row = (force_dir == DIR_NORTH or force_dir == DIR_SOUTH) # The requirement requirement = tiling.requirements[req_index][0] # The cell containing the point cell = requirement.pos[point_index] # The rest of the requirements other_reqs = [ tiling.requirements[i] for i in range(len(tiling.requirements)) if i != req_index ] # Placing the forced occurrence of the point in the requirement new_req, forced_obstructions = requirement.partial_place_forced_point( point_index, force_dir) assert len(new_req) == 1 # New indices of the point. point_cell = (cell[0] if row else cell[0] + 1, cell[1] + 1 if row else cell[1]) # The set of new obstructions, consisting of the forced obstructions, other # obstructions where the point placement has been taken into account and # the 12, 21 in the cell. newobs = forced_obstructions + list( chain.from_iterable( ob.place_point(cell, DIR_NONE, partial=True, row=row) for ob in tiling.obstructions)) + [ Obstruction.single_cell(Perm((0, 1)), point_cell), Obstruction.single_cell(Perm((1, 0)), point_cell) ] # If a point cell, make sure neighbouring cells are empty by adding the # point obstructions. if cell in tiling.point_cells: if force_dir == DIR_EAST or force_dir == DIR_WEST: newobs = (newobs + [ Obstruction.single_cell(Perm((0, )), (point_cell[0] + 1, point_cell[1])), Obstruction.single_cell(Perm((0, )), (point_cell[0] - 1, point_cell[1])) ]) elif force_dir == DIR_NORTH or force_dir == DIR_SOUTH: newobs = (newobs + [ Obstruction.single_cell(Perm((0, )), (point_cell[0], point_cell[1] + 1)), Obstruction.single_cell(Perm((0, )), (point_cell[0], point_cell[1] - 1)) ]) # The new requirements, consisting of the requirement with the point # placed, other requirements where point placement has been taken into # account and the point requirement in the cell. newreqs = [ list( chain.from_iterable( req.place_point(cell, DIR_NONE, partial=True, row=row) for req in reqs)) for reqs in other_reqs ] + [new_req] + [[Requirement.single_cell(Perm((0, )), point_cell)]] return Tiling(obstructions=newobs, requirements=newreqs)
def place_point_of_requirement(tiling, req_index, point_index, force_dir, **kwargs): """ Places the point at point_index in requirement at req_index into tiling. """ if len(tiling.requirements[req_index]) > 1: raise ValueError( "Requirement list at {} contains more than 1 requirement.".format( req_index)) # The requirement requirement = tiling.requirements[req_index][0] # The cell containing the point cell = requirement.pos[point_index] # The rest of the requirements other_reqs = [ tiling.requirements[i] for i in range(len(tiling.requirements)) if i != req_index ] # Placing the forced occurrence of the point in the requirement new_req, forced_obstructions = requirement.place_forced_point( point_index, force_dir) assert len(new_req) == 1 # New indices of the point point_cell = (cell[0] + 1, cell[1] + 1) # The set of new obstructions, consisting of the forced obstructions, other # obstructions where the point placement has been taken into account and # the 12, 21 in the cell. newobs = forced_obstructions + list( chain.from_iterable( ob.place_point(cell, DIR_NONE) for ob in tiling.obstructions)) + [ Obstruction.single_cell(Perm((0, 1)), point_cell), Obstruction.single_cell(Perm((1, 0)), point_cell) ] # The new requirements, consisting of the requirement with the point # placed, other requirements where point placement has been taken into # account and the point requirement in the cell. newreqs = [ list( chain.from_iterable( req.place_point(cell, DIR_NONE) for req in reqs)) for reqs in other_reqs ] + [new_req] + [[Requirement.single_cell(Perm((0, )), point_cell)]] placed_tiling = Tiling(obstructions=newobs, requirements=newreqs) if kwargs.get('regions', False): def cell_map(c): mindex, minval = c maxdex = mindex + 1 maxval = minval + 1 if mindex >= cell[0]: maxdex += 2 if minval >= cell[1]: maxval += 2 if mindex > cell[0]: mindex += 2 if minval > cell[1]: minval += 2 return set([ placed_tiling.forward_map[(x, y)] for x in range(mindex, maxdex) for y in range(minval, maxval) if ((x, y) in placed_tiling.forward_map and placed_tiling. forward_map[(x, y)] in placed_tiling.active_cells) ]) return [placed_tiling], [{c: cell_map(c) for c in tiling.active_cells}] return placed_tiling