def update_anchors(self, custom=None): if custom: clusters = custom else: clusters = self.clusters if not self.hub.cells: for clust in clusters: clust.anchor = None else: for clust in clusters: if not clust.cells: continue # Find node in the hub that is closest to one in the cluster _, anchor = closest_nodes(clust, self.hub) clust.anchor = anchor
def optimize_large_ec(self): for r in range(101): logger.debug("Starting round %d of Ec >> Em", r) if r > 100: raise LoafError("Optimization got lost") balance = self.energy_balance() logger.debug("Current energy balance is %f", balance) c_most = self.highest_energy_cluster(include_hub=False) # get the neighbors of c_most neighbors = [ c for c in self.clusters if (abs(c.cluster_id - c_most.cluster_id) == 1) or ( abs(c.cluster_id - c_most.cluster_id) == self.env.mdc_count - 2) ] assert (len(neighbors) <= 2) and (len(neighbors) > 0) # find the minimum energy neighbor neighbor = min(neighbors, key=lambda x: self.total_cluster_energy(x)) # find the cell in c_most nearest the neighbor c_out, _ = closest_nodes(c_most, neighbor) c_most.remove(c_out) neighbor.add(c_out) # emulate a do ... while loop stdev_new = self.energy_balance() logger.debug("Completed %d rounds of Ec >> Em", r + 1) # if this round didn't reduce balance, then revert the changes and # exit the loop if stdev_new >= balance: neighbor.remove(c_out) c_most.add(c_out) break
def optimization(self): for r in range(101): logger.debug("Starting round %d of optimization", r) if r > 100: raise LoafError("Optimization got lost") balance = self.energy_balance() c_least = self.lowest_energy_cluster() c_most = self.highest_energy_cluster() if self.hub == c_least: _, c_in = closest_nodes([c_most.anchor], c_most) c_most.remove(c_in) self.hub.add(c_in) # check the effects and revert if necessary self.update_anchors() new_balance = self.energy_balance() logger.debug("Completed %d rounds of 2b", r) # if this round didn't reduce stdev, then revert the changes # and exit the loop if new_balance >= balance: self.hub.remove(c_in) c_most.add(c_in) self.update_anchors() break elif self.hub == c_most: # shrink c_most c_out = c_least.anchor if len(self.hub.cells) > 1: self.hub.remove(c_out) c_least.add(c_out) # check the effects and revert if necessary self.update_anchors() new_balance = self.energy_balance() logger.debug("Completed %d rounds of 2b", r) # if this round didn't reduce the energy balance, then revert # the changes and exit the loop. if new_balance >= balance: c_least.remove(c_out) self.hub.add(c_out) self.update_anchors() break else: # shrink c_most _, c_in = closest_nodes([c_most.anchor], c_most) c_most.remove(c_in) self.hub.add(c_in) # grow c_least c_out, _ = closest_nodes(self.hub, [c_least.anchor]) self.hub.remove(c_out) c_least.add(c_out) # check the effects and revert if necessary self.update_anchors() new_balance = self.energy_balance() logger.debug("Completed %d rounds of 2b", r) # if this round didn't reduce stdev, then revert the changes # and exit the loop if new_balance >= balance: c_least.remove(c_out) self.hub.add(c_out) self.hub.remove(c_in) c_most.add(c_in) self.update_anchors() break
def greedy_expansion(self): # First round (initial cell setup and energy calculation) for c in self.cells: c.cluster_id = -1 for vc in self.virtual_clusters: c = LoafCluster(self.env) c.cluster_id = vc.cluster_id c.anchor = self.damaged closest_cell, _ = closest_nodes(vc, self.hub) c.add(closest_cell) self.clusters.append(c) assert self.energy_model.total_movement_energy(self.hub) == 0. # Rounds 2 through N r = 1 while any(not c.completed for c in self.clusters): r += 1 # Determine the minimum-cost cluster by first filtering out all # non-completed clusters. Then find the the cluster with the lowest # total cost. candidates = self.clusters + [self.hub] candidates = [c for c in candidates if not c.completed] c_least = min(candidates, key=lambda x: self.total_cluster_energy(x)) # In general, only consider cells that have not already been added # to a cluster. There is an exception to this when expanding the # hub cluster. cells = [c for c in self.cells if c.cluster_id == -1] # If there are no more cells to assign, then we mark this cluster # as "completed" if not cells: c_least.completed = True logger.debug("All cells assigned. Marking %s as completed", c_least) continue if c_least == self.hub: # This logic handles the case where the hub cluster is has the # fewest energy requirements. Either the cluster will be moved # (initialization) or it will be grown. # # If the hub cluster is still in its original location at the # center of the damaged area, we need to move it to an actual # cell. If the hub has already been moved, then we expand it by # finding the cell nearest to the center of the damaged area, # and that itself hasn't already been added to the hub cluster. if c_least.cells == [self.damaged]: # Find the nearest cell to the center of the damaged area # and move the hub to it. This is equivalent to finding the # cell with the lowest proximity. best_cell = min(cells, key=lambda x: x.proximity) # As the hub only currently has the virtual center cell in # it, we can just "move" the hub to the nearest real cell # by replacing the virtual cell with it. self.hub.remove(self.damaged) self.hub.add(best_cell) # Just for proper bookkeeping, reset the virtual cell's ID # to NOT_CLUSTERED self.damaged.cluster_id = -1 self.damaged.virtual_cluster_id = -1 logger.debug("ROUND %d: Moved %s to %s", r, self.hub, best_cell) else: # Find the set of cells that are not already in the hub # cluster available_cells = list(set(cells) - set(self.hub.cells)) # Out of those cells, find the one that is closest to the # damaged area best_cell, _ = closest_nodes(available_cells, [self.hub.recent]) # Add that cell to the hub cluster self.hub.add(best_cell) logger.debug("ROUND %d: Added %s to %s", r, best_cell, self.hub) self.update_anchors() else: # In this case, the cluster with the lowest energy requirements # is one of the non-hub clusters. best_cell = None # Find the VC that corresponds to the current cluster vci = next(vc for vc in self.virtual_clusters if vc.cluster_id == c_least.cluster_id) # Get a list of the cells that have not yet been added to a # cluster candidates = [c for c in vci.cells if c.cluster_id == -1] if candidates: # Find the cell that is closest to the cluster's recent # cell best_cell, _ = closest_nodes(candidates, [c_least.recent]) else: for i in range(1, max(self.grid.cols, self.grid.rows) + 1): recent = c_least.recent nbrs = self.grid.cell_neighbors(recent, radius=i) for nbr in nbrs: # filter out cells that are not part of a virtual # cluster if nbr.virtual_cluster_id == -1: continue # filter out cells that are not in neighboring VCs dist = abs(nbr.virtual_cluster_id - vci.cluster_id) if dist != 1: continue # if the cell we find is already clustered, we are # done working on this cluster if nbr.cluster_id != -1: c_least.completed = True break best_cell = nbr break if best_cell or c_least.completed: break if best_cell: logger.debug("ROUND %d: Added %s to %s", r, best_cell, c_least) c_least.add(best_cell) else: c_least.completed = True logger.debug( "ROUND %d: No best cell found. Marking %s completed", r, c_least)