def _merge_clusters(scheme, O, S, add_random=True): """Merge bad clusters into 1""" clusters = construct_clusters(scheme, S) new_clusters = [] while clusters: p = random.randint(0, 1) updated_cluster = clusters.pop(0) if not add_random or p: # randomly decide if want to merge curr cluster # always expect split if len(clusters) == 1 cluster_length = len(clusters) for _ in range(cluster_length): if not clusters: break next_cluster = clusters.pop(0) merged = deepcopy(updated_cluster) merged.merge(next_cluster) # if merged cluster is worse than split clusters, do not merge # else, merge if merged.value <= updated_cluster.value + next_cluster.value: clusters.append(next_cluster) else: updated_cluster = merged new_clusters.append(updated_cluster) # fix ids for i in range(len(new_clusters)): new_clusters[i].id = i new_S = Solution.from_clusters(scheme, new_clusters) if satisfies_constraints(scheme, new_S): S = new_S else: # shouldn't happen raise ValueError('infeasible solution in split') return S
def _move_machines(scheme, O, S): """Perform movement of machines between clusters within a solution""" clusters = construct_clusters(scheme, S) rated_machines = _find_best_fit_for_machines(scheme, clusters) while rated_machines: stat = rated_machines.pop(0) curr_id, new_id = stat.curr_cluster, stat.new_cluster if clusters[curr_id].near_empty: # can't move anything out of near empty cluster continue curr_cluster = deepcopy(clusters[curr_id]) new_cluster = deepcopy(clusters[new_id]) curr_cluster.machines, new_cluster.machines = _move( stat.machine, curr_cluster.machines, new_cluster.machines) old_value = clusters[curr_id].value + clusters[new_id].value new_value = curr_cluster.value + new_cluster.value # if new clusters have better objective than old ones, approve move if new_value > old_value: clusters[curr_id] = curr_cluster clusters[new_id] = new_cluster rated_machines = _find_best_fit_for_machines(scheme, clusters) # construct new S # if new S is better and satisfies constraints, approve changes new_S = Solution.from_clusters(scheme, clusters) if O(scheme, new_S) > O(scheme, S) and satisfies_constraints( scheme, new_S): S = new_S return S
def _split_clusters(scheme, O, S, add_random=True): """Split bad clusters in solution""" clusters = construct_clusters(scheme, S) new_clusters = [] for cluster in clusters: p = random.randint(0, 1) if not cluster.can_split: # copy "as is" if can't split the cluster new_clusters.append(cluster) continue if add_random and len(clusters) > 1 and p: # randomly decide if want to split curr cluster # always expect split if len(clusters) == 1 new_clusters.append(cluster) continue new_clusters += _split(scheme, cluster) # fix ids: for i in range(len(new_clusters)): new_clusters[i].id = i new_S = Solution.from_clusters(scheme, new_clusters) if satisfies_constraints(scheme, new_S): S = new_S else: # shouldn't happen raise ValueError('infeasible solution in split') return S