def test_area_to_rect_bad(area, bound_width, bound_height, min_ratio): with pytest.raises(ValueError): area_to_rect(area, bound_width, bound_height, min_ratio)
def test_area_to_rect(area, bound_width, bound_height, min_ratio, wh): assert area_to_rect(area, bound_width, bound_height, min_ratio) == wh
def _alloc_boards_possible(self, boards, min_ratio=0.0, max_dead_boards=None, max_dead_links=None, require_torus=False): """Is it guaranteed that the specified allocation *could* succeed if enough of the machine is free? This function may be conservative. If the specified request would fail when no resources have been allocated, we return False, even if some circumstances the allocation may succeed. For example, if one board in each of the four corners of the machine is dead, no allocation with max_dead_boards==0 can succeed when the machine is empty but may succeed if some other allocation has taken place. Parameters ---------- boards : int The *minimum* number of boards, must be at least 1. Note that if only 1 board is required, :py:class:`._alloc_board` would be a more appropriate function since this function may return as many as three boards when only a single one is requested. min_ratio : float The aspect ratio which the allocated region must be 'at least as square as'. Set to 0.0 for any allowable shape. max_dead_boards : int or None The maximum number of broken or unreachable boards to allow in the allocated region. If None, any number of dead boards is permitted, as long as the board on the bottom-left corner is alive (Default: None). max_dead_links : int or None The maximum number of broken links allow in the allocated region. When require_torus is True this includes wrap-around links, otherwise peripheral links are not counted. If None, any number of broken links is allowed. (Default: None). require_torus : bool If True, only allocate blocks with torus connectivity. In general this will only succeed for requests to allocate an entire machine (when the machine is otherwise not in use!). (Default: False) Returns ------- bool See Also -------- alloc_possible : The (public) wrapper which also supports checking triad allocations. """ # Convert number of boards to number of triads (rounding up...) triads = int(ceil(boards / 3.0)) # Sanity check: can't be a non-machine if triads <= 0: return False # Special case: If a torus is required this is only deliverable when # the requirements match the size of the machine exactly. if require_torus and (triads != self.width * self.height): return False # If no region of the right shape can be made, just fail wh = area_to_rect(triads, self.width, self.height, min_ratio) if wh is None: return False width, height = wh # Test to see whether the allocation could succeed in the idle machine cf = _CandidateFilter(self.width, self.height, self.dead_boards, self.dead_links, max_dead_boards, max_dead_links, require_torus, boards) for x, y in set([(0, 0), (self.width - width, 0), (0, self.height - height), (self.width - width, self.height - height)]): if cf(x, y, width, height): return True # No possible allocation could be made... return False
def alloc_area(self, area, min_ratio=0.0, candidate_filter=None): """Attempt to allocate a rectangular region with at least the specified area which is 'at least as square' as the specified aspect ratio. Parameters ---------- area : int The *minimum* area to allocate, must be at least 1. min_ratio : float The aspect ratio which the allocated region must be 'at least as square as'. Set to 0.0 for any allowable shape. candidate_filter : None or function(x, y, w, h) -> bool A function which will be called with candidate allocations. If the function returns False, the allocation is rejected and the allocator will attempt to find another. If the function returns True, the allocator will then create the allocation. This function may, for example, check that the suggested region is fully connected or does not have too many faults. If this argument is None (the default) the first candidate allocation found will be returned. Returns ------- allocation : (x, y, w, h) or None If the allocation request was met, a tuple giving the position of the bottom-left corner, width and height of the allocation is returned. If the request could not be met, None is returned and no allocation is made. """ # If this node is already populated, give up if self.allocated: return None # Allocation simply can't fit fail fast if area > self.width * self.height: return None # If this node is split (i.e. has children), try inserting into the # children. if self.children is not None: # Try the smallest child first for child in sorted(self.children, key=(lambda c: c.width * c.height)): allocation = child.alloc_area(area, min_ratio, candidate_filter) if allocation: return allocation else: # No child could fit the allocation, fail return None # This is a child node, try to work out a suitable size for the # allocation if possible rect = area_to_rect(area, self.width, self.height, min_ratio) if not rect: return None # Try allocating that size width, height = rect allocation = self.alloc(width, height, candidate_filter) if allocation: x, y = allocation return (x, y, width, height) else: return None