def optimize_rendezvous_points(self): # self.show_state() round_count = 1 while self._unbalanced(): logger.debug("Running optimization round %d", round_count) round_count += 1 if round_count > 100: raise TimeoutError("TOCS optimization got lost") average_length = self.average_tour_length() for clust in self.clusters: logger.debug("Examining %s", clust) if much_greater_than(average_length, clust.tour_length, r=self._length_threshold): # Handle the case where we need to move the cluster's RP # closer to the centroid. self._grow_cluster(clust, average_length) elif much_greater_than(clust.tour_length, average_length, r=self._length_threshold): # Handle the case where we need to move the cluster's RP # closer to the cluster itself. self._shrink_cluster(clust, average_length) else: continue
def _unbalanced(self): """ Determine if any clusters have tours which are dramatically smaller or larger than the average tour length over all clusters. :return: True if the clusters are unbalanced and need adjustment :return: False if the clusters are all close to even tour lengths :rtype: bool """ average_length = self.average_tour_length() logger.debug("Average tour length: %s", average_length) for clust in self.clusters: logger.debug("%s tour length: %s", clust, clust.tour_length) max_tour = clust.tour_length max_tour += np.linalg.norm( clust.rendezvous_point.location.nd - self.center) if max_tour < average_length: logger.debug("Cannot optimize %s in this round", clust) continue if much_greater_than(average_length, clust.tour_length, r=self._length_threshold): return True elif much_greater_than(clust.tour_length, average_length, r=self._length_threshold): return True return False
def compute_paths(self): self.find_cells() self.create_virtual_clusters() # Check for the case where Em >> Ec clusters = list() for vc in self.virtual_clusters: cluster = LoafCluster(self.env) cluster.cluster_id = vc.cluster_id for cell in vc.cells: cluster.add(cell) clusters.append(cluster) self.update_anchors(clusters) e_c = np.sum([ self.energy_model.total_comms_energy(cluster) for cluster in clusters ]) # * pq.J e_m = np.sum([ self.energy_model.total_movement_energy(cluster) for cluster in clusters ]) # * pq.J logger.debug("Em is %s", e_m) logger.debug("Ec is %s", e_c) if much_greater_than(e_m, e_c): logger.debug("Em >> Ec, running special case") self.em_is_large = True self.clusters = clusters self.update_anchors() return self elif much_greater_than(e_c, e_m): logger.debug("Ec >> Em, running special case") self.ec_is_large = True self.clusters = clusters self.update_anchors() self.optimize_large_ec() else: logger.debug("Proceeding with standard optimization") self.greedy_expansion() self.optimization() # self.greedy_expansion() # self.optimization() return self
def _shrink_cluster(self, clust, average_length): """ Update a cluster's rendezvous point so as to bring it "further" from the centroid cluster. This effectively shrinks the cluster's tour. The process of moving the RP is repeated until the cluster's tour length is no longer "much smaller" than the average tour length. :param clust: Cluster to consider :type clust: ToCSCluster :param average_length: Average tour length of all clusters :type average_length: pq.quantity.Quantity :return: None """ while much_greater_than(clust.tour_length, average_length, r=self._length_threshold): current_rp_loc = clust.rendezvous_point.location.nd # Calculate the vector between the current RP and the center of the # centroid cluster. Then just scale it up. new_rp_loc = current_rp_loc - self.center new_rp_loc *= 1.25 new_rp_loc += self.center # Handle the case where a cluster's tour cannot be optimized any # further. if np.allclose(new_rp_loc, current_rp_loc): break # Now assign the new location to the existing RP object (just saves # us object creation). self._update_rp_pos(clust, new_rp_loc) # Now reassign segments if needed self._reassign_segments_to_central(clust)
def _grow_cluster(self, clust, average_length): """ Update a cluster's rendezvous point so as to bring it "closer" to the centroid cluster. This effectively grows the cluster's tour. The process of moving the RP is repeated until the cluster's tour length is no longer "much larger" than the average tour length. :param clust: Cluster to consider :type clust: ToCSCluster :param average_length: Average tour length of all clusters :type average_length: pq.quantity.Quantity :return: None """ while much_greater_than(average_length, clust.tour_length, r=self._length_threshold): current_rp_loc = clust.rendezvous_point.location.nd # Calculate the vector between the current RP and the center of the # centroid cluster. Then just scale it down. new_rp_loc = current_rp_loc - self.center new_rp_loc *= 0.75 new_rp_loc += self.center if np.allclose(current_rp_loc, new_rp_loc): break # Now assign the new location to the existing RP object (just saves # us object creation). self._update_rp_pos(clust, new_rp_loc) # Now reassign segments if the cluster's RP is closer to the # centroid than an actual segment within the central cluster. self._reassign_segments_to_cluster(clust)