|  | """ Grid splitting model with connection database back references. | 
|  |  | 
|  | The Grid object provides methods to manipulate a 2D grid of Tile objects, that | 
|  | contain zero or more Site objects. Site objects are considered immutable. | 
|  |  | 
|  | To construct the Grid object, the initial grid and an empty_tile_type_pkey must | 
|  | be provided.  The initial grid should be provided as a map of 2 element int | 
|  | tuples to Tile objects.  Tile objects should already contain their initial | 
|  | sites prior to construction of the Grid object. | 
|  |  | 
|  | """ | 
|  | from enum import Enum | 
|  | from collections import namedtuple | 
|  |  | 
|  |  | 
|  | class Direction(Enum): | 
|  | """ Grid directions. """ | 
|  | NORTH = 1 | 
|  | SOUTH = 2 | 
|  | EAST = 3 | 
|  | WEST = 4 | 
|  |  | 
|  |  | 
|  | NORTH = Direction.NORTH | 
|  | SOUTH = Direction.SOUTH | 
|  | EAST = Direction.EAST | 
|  | WEST = Direction.WEST | 
|  |  | 
|  | OPPOSITE_DIRECTIONS = { | 
|  | NORTH: SOUTH, | 
|  | SOUTH: NORTH, | 
|  | EAST: WEST, | 
|  | WEST: EAST, | 
|  | } | 
|  |  | 
|  | # Zipper direction when splitting in a direction | 
|  | SPLIT_NEXT_DIRECTIONS = { | 
|  | NORTH: EAST, | 
|  | SOUTH: EAST, | 
|  | EAST: SOUTH, | 
|  | WEST: SOUTH, | 
|  | } | 
|  |  | 
|  |  | 
|  | def opposite_direction(direction): | 
|  | """ Return opposite direction of given direction. | 
|  |  | 
|  | >>> opposite_direction(NORTH) | 
|  | <Direction.SOUTH: 2> | 
|  | >>> opposite_direction(SOUTH) | 
|  | <Direction.NORTH: 1> | 
|  | >>> opposite_direction(EAST) | 
|  | <Direction.WEST: 4> | 
|  | >>> opposite_direction(WEST) | 
|  | <Direction.EAST: 3> | 
|  |  | 
|  | """ | 
|  | return OPPOSITE_DIRECTIONS[direction] | 
|  |  | 
|  |  | 
|  | # Right handed coordinate system, N/S in y, E/W in x, E is x-positive, | 
|  | # S is y-positive. | 
|  | DIRECTION_OFFSET = { | 
|  | NORTH: [0, -1], | 
|  | SOUTH: [0, 1], | 
|  | EAST: [1, 0], | 
|  | WEST: [-1, 0], | 
|  | } | 
|  |  | 
|  |  | 
|  | def coordinate_in_direction(coord, direction): | 
|  | """ Given a coordinate, returns a new coordinate 1 step in direction. | 
|  |  | 
|  | Coordinate system is right handed, N/S in y, E/W in x.  E is x-positive. | 
|  | S is y-positive. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | coord : Tuple of 2 ints | 
|  | Starting coordinate | 
|  | direction : Direction | 
|  | Direction to add unit vector. | 
|  |  | 
|  | Returns | 
|  | ------- | 
|  | Tuple of 2 ints | 
|  | Coordinate 1 unit step in specified direction.  Will return none if | 
|  | x or y coordinate is negative. | 
|  |  | 
|  | Examples | 
|  | -------- | 
|  |  | 
|  | >>> coordinate_in_direction((0, 0), SOUTH) | 
|  | (0, 1) | 
|  | >>> coordinate_in_direction((0, 0), EAST) | 
|  | (1, 0) | 
|  | >>> coordinate_in_direction((1, 1), SOUTH) | 
|  | (1, 2) | 
|  | >>> coordinate_in_direction((1, 1), NORTH) | 
|  | (1, 0) | 
|  | >>> coordinate_in_direction((1, 1), EAST) | 
|  | (2, 1) | 
|  | >>> coordinate_in_direction((1, 1), WEST) | 
|  | (0, 1) | 
|  |  | 
|  | # Returns None for negative coordinates. | 
|  | >>> coordinate_in_direction((0, 0), NORTH) | 
|  | >>> coordinate_in_direction((1, 0), NORTH) | 
|  | >>> coordinate_in_direction((0, 0), WEST) | 
|  | >>> coordinate_in_direction((0, 1), WEST) | 
|  |  | 
|  | """ | 
|  | x, y = coord | 
|  | dx, dy = DIRECTION_OFFSET[direction] | 
|  | x += dx | 
|  | y += dy | 
|  |  | 
|  | if x < 0 or y < 0: | 
|  | return None | 
|  | else: | 
|  | return x, y | 
|  |  | 
|  |  | 
|  | class Site(namedtuple('Site', ('name', 'phy_tile_pkey', 'tile_type_pkey', | 
|  | 'site_type_pkey', 'site_pkey', 'x', 'y'))): | 
|  | """ Object to hold back reference information for a site. """ | 
|  | pass | 
|  |  | 
|  |  | 
|  | class Tile(object): | 
|  | """ Tile instance within the grid. | 
|  |  | 
|  | Attributes | 
|  | ---------- | 
|  | root_phy_tile_pkeys : list of ints | 
|  | The list of root_phy_tile_pkey's. | 
|  |  | 
|  | By default a tile typically has one root phy_tile_pkey, which is the | 
|  | phy_tile this initial represents. | 
|  |  | 
|  | If two Tile objects are merged, the root_phy_tile_pkeys are also merged. | 
|  | If a Tile object is split, only one of the split tiles will take all | 
|  | of the root_phy_tile_pkeys, and the other tiles will have no | 
|  | root_phy_tile_pkeys. | 
|  |  | 
|  | Invariant: Each phy_tile_pkey will appear in 1 and only 1 Tile | 
|  | object's root_phy_tile_pkeys list. | 
|  |  | 
|  | Because of the invariant, the root_phy_tile_pkeys list can be used as | 
|  | a default assignment of children of the relevant phy_tile_pkey items | 
|  | (e.g. wires, pips, sites, etc). | 
|  |  | 
|  | phy_tile_pkeys : list of ints | 
|  | The list of phy_tile_pkey's.  This is the list of all phy_tile_pkey's | 
|  | that are involved in this tile via either a tile merge or split. | 
|  |  | 
|  | By default a tile typically has one phy_tile_pkey, which is the | 
|  | phy_tile this initial represents. | 
|  |  | 
|  | If two Tile objects are split, all output tiles will get a copy of the | 
|  | original phy_tile_pkeys list.  This attribute can be used to determine | 
|  | what phy_tile_pkeys were used to make this tile. | 
|  |  | 
|  | sites : list of Site objects | 
|  | This is the list of Site's contained within this tile.  This should | 
|  | be initial set to the Site objects contained within the original | 
|  | phy_tile. | 
|  |  | 
|  | Invariant: Each Site object will be contained within exactly one Tile | 
|  | object. | 
|  |  | 
|  | split_sites : boolean | 
|  | True if this tile was split. | 
|  |  | 
|  | Invariant: Each split tile will contain exactly one Site object. | 
|  | Invariant: Two tiles that were split cannot be merged, otherwise the | 
|  | resulting Tile will have two Sites, potentially from different | 
|  | phy_tile_pkey, which cannot be presented using FASm prefixes. | 
|  |  | 
|  | neighboors : Map of Direction to Tile object | 
|  | Linked list pointers to neighboors tiles. | 
|  |  | 
|  | Invariant: Underlying linked link should be rectangular after an | 
|  | operation on the grid.  An single operation on the Tile will typically | 
|  | invalidate the overall grid, it is up to the Grid object to enforce | 
|  | the rectangular constraint. | 
|  |  | 
|  | Invariant: Underlying linked link must not be circular. | 
|  |  | 
|  | """ | 
|  |  | 
|  | def __init__( | 
|  | self, root_phy_tile_pkeys, phy_tile_pkeys, tile_type_pkey, sites | 
|  | ): | 
|  | self.root_phy_tile_pkeys = root_phy_tile_pkeys | 
|  | self.phy_tile_pkeys = phy_tile_pkeys | 
|  | self.tile_type_pkey = tile_type_pkey | 
|  | self.sites = sites | 
|  | self.split_sites = False | 
|  | self.neighboors = {} | 
|  |  | 
|  | def link_neighboor_in_direction(self, other_tile, direction_to_other_tile): | 
|  | """ Connect this tile to another tile in a specific direction. | 
|  |  | 
|  | It is legal to call this method on an existing connection, but it is | 
|  | not legal to call this method to replace an existing connection. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | other_tile : Tile object | 
|  | Other Tile object to connect in specified direction. | 
|  | direction_to_other_tile : Direction | 
|  | Direction to connect other tile. | 
|  | """ | 
|  | if direction_to_other_tile in self.neighboors: | 
|  | assert id( | 
|  | self.neighboors[direction_to_other_tile] | 
|  | ) == id(other_tile), (self.neighboors, direction_to_other_tile) | 
|  | self.neighboors[direction_to_other_tile] = other_tile | 
|  |  | 
|  | direction_to_this_tile = opposite_direction(direction_to_other_tile) | 
|  | if direction_to_this_tile in other_tile.neighboors: | 
|  | assert id(other_tile.neighboors[direction_to_this_tile] | 
|  | ) == id(self) | 
|  |  | 
|  | other_tile.neighboors[direction_to_this_tile] = self | 
|  |  | 
|  | def insert_in_direction(self, other_tile, direction_to_other_tile): | 
|  | """ Insert a tile in a specified direction. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | other_tile : Tile object | 
|  | Other Tile object to insert in specified direction. | 
|  | direction_to_other_tile : Direction | 
|  | Direction to insert other tile. | 
|  |  | 
|  | """ | 
|  | old_neighboor = self.neighboors.get(direction_to_other_tile, None) | 
|  |  | 
|  | direction_to_this_tile = opposite_direction(direction_to_other_tile) | 
|  |  | 
|  | self.neighboors[direction_to_other_tile] = other_tile | 
|  | other_tile.neighboors[direction_to_this_tile] = self | 
|  |  | 
|  | if old_neighboor is not None: | 
|  | other_tile.neighboors[direction_to_other_tile] = old_neighboor | 
|  | old_neighboor.neighboors[direction_to_this_tile] = other_tile | 
|  |  | 
|  | def walk_in_direction(self, direction): | 
|  | """ Walk in specified direction from this Tile node. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  |  | 
|  | direction : Direction | 
|  | Direction to walk in. | 
|  |  | 
|  | Yields | 
|  | ------ | 
|  |  | 
|  | tile : Tile | 
|  | Tile in specified direction.  First Tile object will always be the | 
|  | tile whose walk_in_direction was invoked.  When the end of the grid | 
|  | is encounted, no more tiles will be yielded. | 
|  | """ | 
|  |  | 
|  | node = self | 
|  |  | 
|  | while True: | 
|  | yield node | 
|  | if direction in node.neighboors: | 
|  | node = node.neighboors[direction] | 
|  | else: | 
|  | break | 
|  |  | 
|  |  | 
|  | def check_grid_loc(grid_loc_map): | 
|  | """ Verifies input grid makes sense. | 
|  |  | 
|  | Internal grid consistency is defined as: | 
|  | - Has an origin location @ (0, 0) | 
|  | - Is rectangular | 
|  | - Has no gaps. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | grid_loc_map : Dict of 2 int tuple to Tile objects | 
|  | Grid being checked. | 
|  |  | 
|  | Raises | 
|  | ------ | 
|  | AssertionError | 
|  | If provided grid does not conform to assumptions about grid. | 
|  |  | 
|  | """ | 
|  | xs, ys = zip(*grid_loc_map.keys()) | 
|  |  | 
|  | max_x = max(xs) | 
|  | max_y = max(ys) | 
|  |  | 
|  | for x in range(max_x + 1): | 
|  | for y in range(max_y + 1): | 
|  | assert (x, y) in grid_loc_map, (x, y) | 
|  |  | 
|  |  | 
|  | def build_mesh(current, visited, loc, grid_loc_map): | 
|  | """ Stitch grid_loc_map into a double-linked list 2D mesh. | 
|  |  | 
|  | Modifies Tile object neighboors attributes to form a doubly linked list | 
|  | 2D mesh. | 
|  |  | 
|  | It is strongly recommended that grid_loc_map be passed to check_grid_loc | 
|  | prior to calling build_mesh to verify grid invariants. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | current : Tile object | 
|  | visited : set of python object id's | 
|  | Should be empty on root invocation. | 
|  | loc : Location of current Tile object argument | 
|  | grid_loc_map : Dict of 2 int tuple to Tile objects | 
|  | Grid being converted to linked list form. | 
|  |  | 
|  | """ | 
|  |  | 
|  | for direction in (SOUTH, EAST): | 
|  | new_loc = coordinate_in_direction(loc, direction) | 
|  | if new_loc in grid_loc_map: | 
|  | current.link_neighboor_in_direction( | 
|  | grid_loc_map[new_loc], direction | 
|  | ) | 
|  | if id(grid_loc_map[new_loc]) not in visited: | 
|  | visited.add(id(grid_loc_map[new_loc])) | 
|  | build_mesh( | 
|  | grid_loc_map[new_loc], visited, new_loc, grid_loc_map | 
|  | ) | 
|  |  | 
|  |  | 
|  | class Grid(object): | 
|  | """ Object for manipulating a 2D grid of Tile objects. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  |  | 
|  | grid_loc_map : Dict of 2 int tuple to Tile objects | 
|  | Initial grid of Tile objects. | 
|  | empty_tile_type_pkey : int | 
|  | tile_type_pkey to use when creating new empty tiles during tile splits. | 
|  |  | 
|  | """ | 
|  |  | 
|  | def __init__(self, grid_loc_map, empty_tile_type_pkey): | 
|  | # Make sure initial grid is sane | 
|  | check_grid_loc(grid_loc_map) | 
|  |  | 
|  | # Keep root object of grid. | 
|  | self.origin = grid_loc_map[(0, 0)] | 
|  |  | 
|  | # Convert grid to doubly-linked list. | 
|  | build_mesh(self.origin, set(), (0, 0), grid_loc_map) | 
|  |  | 
|  | # Keep list of all Tile objects for convience. | 
|  | self.items = grid_loc_map.values() | 
|  |  | 
|  | self.empty_tile_type_pkey = empty_tile_type_pkey | 
|  |  | 
|  | def column(self, x): | 
|  | """ Return Tile object at top of column. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | x : int | 
|  | 0 based column to retrive. | 
|  |  | 
|  | Returns | 
|  | ------- | 
|  | top_of_column : Tile | 
|  | Tile object at top of column | 
|  |  | 
|  | """ | 
|  | top_of_column = self.origin | 
|  |  | 
|  | for _ in range(x): | 
|  | top_of_column = top_of_column.neighboors[EAST] | 
|  |  | 
|  | return top_of_column | 
|  |  | 
|  | def row(self, y): | 
|  | """ Return Tile object at right of row. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | y : int | 
|  | 0 based row to retrive. | 
|  |  | 
|  | Returns | 
|  | ------- | 
|  | right_of_row : Tile | 
|  | Tile object at right of row | 
|  |  | 
|  | """ | 
|  | right_of_row = self.origin | 
|  |  | 
|  | for _ in range(y): | 
|  | right_of_row = right_of_row.neighboors[SOUTH] | 
|  |  | 
|  | def split_tile(self, tile, tile_type_pkeys, split_direction, split_map): | 
|  | """ Split tile in specified direction. | 
|  |  | 
|  | This method requires that the tiles required to perform the split (e.g. | 
|  | len(tile_type_pkeys)-1 tiles in split_direction from tile) have | 
|  | tile_type_pkey == empty_tile_type_pkey, e.g. they are empty tiles. | 
|  |  | 
|  | If empty tiles must be inserted into the grid to accomidate the split, | 
|  | this must be done prior to calling this method. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | tile : Tile object | 
|  | Tile being split | 
|  | tile_type_pkeys : List of int | 
|  | List of new tile_type_pkeys to be used after the tile split. | 
|  | The tile being split will become tile_type_pkeys[0], the next tile | 
|  | in split_direction will become tile_type_pkeys[1], etc. | 
|  | split_direction : Direction | 
|  | Which direction from tile should the split occur. | 
|  | split_map : Dict of (int, int) to int | 
|  | Mapping of site location (x, y) to tile_type_pkey indicies. | 
|  | This enables control over which sites go to which tiles based on | 
|  | their coordinate. | 
|  |  | 
|  | min(split_map.values()) >= 0 | 
|  | max(split_map.values()) < len(tile_type_pkeys) | 
|  |  | 
|  | """ | 
|  | sites = tile.sites | 
|  | tile.tile_type_pkey = self.empty_tile_type_pkey | 
|  | phy_tile_pkeys = set(tile.phy_tile_pkeys) | 
|  | new_tiles = [] | 
|  |  | 
|  | for idx, tile in enumerate(tile.walk_in_direction(split_direction)): | 
|  | assert tile.tile_type_pkey == self.empty_tile_type_pkey, ( | 
|  | tile.tile_type_pkey | 
|  | ) | 
|  | tile.phy_tile_pkeys = [] | 
|  |  | 
|  | new_tiles.append(tile) | 
|  |  | 
|  | if idx + 1 >= len(tile_type_pkeys): | 
|  | break | 
|  |  | 
|  | for tile, new_tile_type_pkey in zip(new_tiles, tile_type_pkeys): | 
|  | assert tile.tile_type_pkey == self.empty_tile_type_pkey | 
|  |  | 
|  | tile.tile_type_pkey = new_tile_type_pkey | 
|  | tile.phy_tile_pkeys = list( | 
|  | set(tile.phy_tile_pkeys) | phy_tile_pkeys | 
|  | ) | 
|  | tile.sites = [] | 
|  | tile.split_sites = True | 
|  |  | 
|  | for site in sites: | 
|  | site_idx = split_map[site.x, site.y] | 
|  | assert site_idx < len(tile_type_pkeys), ( | 
|  | site, site_idx, tile_type_pkeys | 
|  | ) | 
|  | new_tiles[site_idx].sites.append(site) | 
|  |  | 
|  | def insert_empty(self, top, insert_in_direction): | 
|  | """ Insert empty row/colum. | 
|  |  | 
|  | Insert a row/column of empty tiles from the tiles in the row/column specified | 
|  | by top_of_row/column tile.  The new empty tiles will have tile_type_pkey | 
|  | set to empty_tile_type_pkey, and have phy_tile_pkeys of the tile they | 
|  | were inserted from. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | top_of_row/column : Tile object | 
|  | Tile at top of row/column adjcent to where new row/column should be | 
|  | inserted. | 
|  | insert_in_direction : Direction | 
|  | Direction to insert empty tiles, from perspective of the row/column | 
|  | specified by top_of_row/column. | 
|  |  | 
|  | """ | 
|  | # Verify that insert direction is not the same as zipper direction. | 
|  | next_dir = SPLIT_NEXT_DIRECTIONS[insert_in_direction] | 
|  |  | 
|  | # Verify that top is in fact the top of the zipper | 
|  | assert OPPOSITE_DIRECTIONS[next_dir] not in top.neighboors | 
|  |  | 
|  | empty_tiles = [] | 
|  | for tile in top.walk_in_direction(next_dir): | 
|  | empty_tile = Tile( | 
|  | root_phy_tile_pkeys=[], | 
|  | phy_tile_pkeys=list(tile.phy_tile_pkeys), | 
|  | tile_type_pkey=self.empty_tile_type_pkey, | 
|  | sites=[] | 
|  | ) | 
|  | empty_tiles.append(empty_tile) | 
|  |  | 
|  | tile.insert_in_direction(empty_tile, insert_in_direction) | 
|  |  | 
|  | for a, b in zip(empty_tiles, empty_tiles[1:]): | 
|  | a.link_neighboor_in_direction(b, next_dir) | 
|  |  | 
|  | self.check_grid() | 
|  |  | 
|  | def split_in_dir( | 
|  | self, | 
|  | top, | 
|  | tile_type_pkey, | 
|  | tile_type_pkeys, | 
|  | split_direction, | 
|  | split_map, | 
|  | ): | 
|  | """ Split row/column of tiles. | 
|  |  | 
|  | Splits specified tile types into new row/column by first inserting any | 
|  | required empty row/column in the split direction, and then performing | 
|  | the split. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | top_of_row/column : Tile object | 
|  | Tile at top of row/column where split should be performed. | 
|  | tile_type_pkey : Tile type to split. | 
|  | tile_type_pkeys : Refer to split_tile documentation. | 
|  | split_direction : Direction | 
|  | Direction to insert perform split.  New row/column will be inserted | 
|  | in that direction to accomidate the tile split. | 
|  | split_map : Dict of (int, int) to int | 
|  | Mapping of site location (x, y) to tile_type_pkey indicies. | 
|  | This enables control over which sites go to which tiles based on | 
|  | their coordinate. | 
|  |  | 
|  | """ | 
|  | next_dir = SPLIT_NEXT_DIRECTIONS[split_direction] | 
|  |  | 
|  | # Find how many empty tiles are required to support the split | 
|  | num_to_insert = 0 | 
|  | for tile in top.walk_in_direction(next_dir): | 
|  | if tile.tile_type_pkey != tile_type_pkey: | 
|  | continue | 
|  |  | 
|  | for idx, tile_in_split in enumerate( | 
|  | tile.walk_in_direction(split_direction)): | 
|  | if idx == 0: | 
|  | continue | 
|  | else: | 
|  | if tile_in_split.tile_type_pkey != self.empty_tile_type_pkey: | 
|  | num_to_insert = max(num_to_insert, idx) | 
|  |  | 
|  | if idx + 1 >= len(tile_type_pkeys): | 
|  | break | 
|  |  | 
|  | for _ in range(num_to_insert): | 
|  | self.insert_empty(top, split_direction) | 
|  |  | 
|  | for tile in top.walk_in_direction(next_dir): | 
|  | if tile.tile_type_pkey != tile_type_pkey: | 
|  | continue | 
|  |  | 
|  | self.split_tile(tile, tile_type_pkeys, split_direction, split_map) | 
|  |  | 
|  | def split_tile_type( | 
|  | self, tile_type_pkey, tile_type_pkeys, split_direction, split_map | 
|  | ): | 
|  | """ Split a specified tile type within grid. | 
|  |  | 
|  | Splits specified tile types by finding each column that contains the | 
|  | relevant tile type, and spliting each column. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | tile_type_pkey : Tile type to split. | 
|  | tile_type_pkeys : Refer to split_tile documentation. | 
|  | split_direction : Direction | 
|  | Direction to insert perform split. | 
|  | split_map : Dict of (int, int) to int | 
|  | Mapping of site location (x, y) to tile_type_pkey indicies. | 
|  | This enables control over which sites go to which tiles based on | 
|  | their coordinate. | 
|  |  | 
|  | """ | 
|  | tiles_seen = set() | 
|  | tiles = [] | 
|  | tops_to_split = [] | 
|  |  | 
|  | next_dir = SPLIT_NEXT_DIRECTIONS[split_direction] | 
|  |  | 
|  | for tile in self.items: | 
|  | if id(tile) in tiles_seen: | 
|  | continue | 
|  |  | 
|  | if tile.tile_type_pkey == tile_type_pkey: | 
|  | # Found a row/column that needs to be split, walk to the bottom of | 
|  | # the row/column, then back to the top. | 
|  | for tile in tile.walk_in_direction(next_dir): | 
|  | pass | 
|  |  | 
|  | for tile in tile.walk_in_direction( | 
|  | OPPOSITE_DIRECTIONS[next_dir]): | 
|  | if id(tile) not in tiles_seen: | 
|  | tiles_seen.add(id(tile)) | 
|  | if tile.tile_type_pkey == tile_type_pkey: | 
|  | tiles.append(tile) | 
|  |  | 
|  | tops_to_split.append(tile) | 
|  |  | 
|  | for top in tops_to_split: | 
|  | self.split_in_dir( | 
|  | top, tile_type_pkey, tile_type_pkeys, split_direction, | 
|  | split_map | 
|  | ) | 
|  |  | 
|  | def merge_in_dir(self, tile, merge_direction): | 
|  | """ Merge tile in specified direction. | 
|  |  | 
|  | Merging a tile causes the connects of that tile to be merged into | 
|  | the tile in the merge direction.  The tile that was merged will become | 
|  | empty. | 
|  |  | 
|  | The original tile root_phy_tile_pkeys and phy_tile_pkeys will appear | 
|  | first. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | tile : Tile | 
|  | Tile to merge | 
|  | merge_direction : Direction | 
|  | Direction to merge tiles. | 
|  | """ | 
|  | assert merge_direction in tile.neighboors, (tile, merge_direction) | 
|  |  | 
|  | merge_into = tile.neighboors[merge_direction] | 
|  |  | 
|  | merge_into.root_phy_tile_pkeys.extend(tile.root_phy_tile_pkeys) | 
|  | merge_into.phy_tile_pkeys.extend(tile.phy_tile_pkeys) | 
|  | merge_into.sites.extend(tile.sites) | 
|  |  | 
|  | tile.sites = list() | 
|  | tile.tile_type_pkey = self.empty_tile_type_pkey | 
|  | tile.root_phy_tile_pkeys = list() | 
|  | tile.phy_tile_pkeys = list() | 
|  |  | 
|  | def merge_tile_type(self, tile_type_pkey, merge_direction): | 
|  | """ Merge tile types in specified direction. | 
|  |  | 
|  | Parameters | 
|  | ---------- | 
|  | tile_type_pkey : Tile type to split. | 
|  | merge_direction : Direction | 
|  | Direction to merge tiles. | 
|  |  | 
|  | """ | 
|  | for tile in self.items: | 
|  | if tile.tile_type_pkey == tile_type_pkey: | 
|  | self.merge_in_dir(tile, merge_direction) | 
|  |  | 
|  | def output_grid(self): | 
|  | """ Convert grid back to coordinate lookup form. | 
|  |  | 
|  | Returns | 
|  | ------- | 
|  | grid_loc_map : Dict of 2 int tuple to Tile objects | 
|  | Output grid of Tile objects. | 
|  |  | 
|  | """ | 
|  | grid_loc_map = {} | 
|  | for x, tile in enumerate(self.origin.walk_in_direction(EAST)): | 
|  | for y, tile in enumerate(tile.walk_in_direction(SOUTH)): | 
|  | grid_loc_map[(x, y)] = tile | 
|  |  | 
|  | check_grid_loc(grid_loc_map) | 
|  |  | 
|  | return grid_loc_map | 
|  |  | 
|  | def check_grid(self): | 
|  | """ Verifies that grid linked list model still represents valid grid. | 
|  | """ | 
|  | self.output_grid() |