From c567f43540bbe3168efe59e34122ab5b315ae348 Mon Sep 17 00:00:00 2001 From: Anne-Liza <a.m.r.m.bruggeman@student.tudelft.nl> Date: Tue, 31 Jul 2018 10:42:56 +0200 Subject: [PATCH] Improved performance of get_runtime_sequence() to reduce its calculation time Former-commit-id: e7853586b567b273a2fcf925cb35b29f37388f7d --- kadmos/graph/graph_data.py | 90 ++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/kadmos/graph/graph_data.py b/kadmos/graph/graph_data.py index afe796ecf..e7abc5fa0 100644 --- a/kadmos/graph/graph_data.py +++ b/kadmos/graph/graph_data.py @@ -786,17 +786,18 @@ class DataGraph(KadmosGraph): return function_order - def get_time_line(self, sequence, use_runtime_info=False, coupling_dict=None): - """Function to get the time line of a sequence of nodes. At each time step it is indicated which nodes are - waiting for data, which nodes are running and which nodes are finished. Each node starts running as soon as all - its required input data is available. + def get_runtime_sequence(self, sequence, use_runtime_info=False, coupling_dict=None, get_time_line=False): + """Function to get the runtime of a sequence of nodes. Each node starts running as soon as all its required + input data is available. - :param sequence: Sequence of nodes + :param sequence: sequence of nodes :type sequence: list - :param use_runtime_info: option to use the runtime of the disciplines, if False the runtime is set to 1 + :param use_runtime_info: option to use the runtime of the nodes, if False the runtime for each node is set to 1 :type use_runtime_info: bool :param coupling_dict: coupling dictionary of the graph :type coupling_dict: dict + :param get_time_line: option to return a time line which indicates at each time step which nodes are waiting + for input, which nodes are running and which nodes are finished """ # Input assertion @@ -807,67 +808,60 @@ class DataGraph(KadmosGraph): # If zero nodes, return zero runtime if not sequence: - return [[0, [], [], []]] + if not get_time_line: + return [[0, [], [], []]] + else: + return 0 # Get coupling dictionary if not coupling_dict: coupling_dict = self.get_coupling_dictionary() - # Calculate runtime - waiting_list = list(sequence) + # Initialize variables + waiting_list = dict() running = dict() - finished = [] + finished = set() time_line = [] total_runtime = 0 + # Add required input and runtime to each node in the waiting list + for node in sequence: + required_input = set(sequence[:sequence.index(node)]).intersection(coupling_dict[node]) + runtime = self.nodes[node]['performance_info']['run_time'] if use_runtime_info else 1 + waiting_list[node] = [required_input, runtime] + while waiting_list or running: if waiting_list: - # Determine for which nodes in the waiting list all info is known, such that they can be executed + updated_waiting_list = dict(waiting_list) for node in waiting_list: - required_input = set(sequence[:sequence.index(node)]).intersection(coupling_dict[node]) - execute = True if all([input_node in finished for input_node in required_input]) else False - if execute: - # If all input is known the node can be moved to the run list - running[node] = self.nodes[node]['performance_info']['run_time'] if use_runtime_info else 1 - # Remove the activated nodes from the waiting list - [waiting_list.pop(waiting_list.index(node)) for node in running if node in waiting_list] + # Check if all input data is available + if waiting_list[node][0].issubset(finished): + # If all input is known the node can be moved to the run list and deleted from the waiting list + running[node] = updated_waiting_list.pop(node)[1] + waiting_list = dict(updated_waiting_list) # Add timestep to the time line - time_line.append([total_runtime, list(waiting_list), running.keys(), list(finished)]) + if get_time_line: + time_line.append([total_runtime, list(waiting_list), running.keys(), list(finished)]) # When the running list is updated, the next time step can be determined - # Determine the shortest runtime in the running list - time_step = min(running.items(), key=lambda x: x[1])[1] + time_step = min(running.values()) # Let all modules run for the time of the time step + updated_running_list = dict(running) for node in running: running[node] -= time_step # If node is finished, move it to the finished list if running[node] == 0: - finished.append(node) + finished.add(node) + del updated_running_list[node] + running = dict(updated_running_list) # Add the time step to the total time total_runtime += time_step - # Remove the finished nodes from the running list - for node in finished: - if node in running: - del running[node] - - # Add final step to time line - time_line.append([total_runtime, list(waiting_list), running.keys(), list(finished)]) - - return time_line - - def get_runtime_sequence(self, sequence, use_runtime_info=False, coupling_dict=None): - """Function to get the total runtime of a sequence of nodes. Each node starts running as soon as all its - required input data is available. - :param sequence: Sequence of nodes for which the runtime needs to be calculated - :type sequence: list - :param use_runtime_info: option to use the runtime of the disciplines, if False the runtime is set to 1 - :type use_runtime_info: bool - :param coupling_dict: coupling dictionary of the graph - :type coupling_dict: dict - """ - - time_line = self.get_time_line(sequence, use_runtime_info, coupling_dict) - return time_line[-1][0] + if get_time_line: + # Add final step to time line + time_line.append([total_runtime, list(waiting_list), running.keys(), list(finished)]) + return time_line + else: + return total_runtime def minimize_feedback(self, nodes, method, multi_start=None, get_evaluations=False, coupling_dict=None, rcb=1, use_runtime_info=False): @@ -1245,8 +1239,9 @@ class DataGraph(KadmosGraph): def _get_lower_bound_branch_and_bound(self, branch, nodes, coupling_dict, use_runtime_info): """Function to calculate the lower bound of a branch in the branch and bound algorithm. - The lower bound is defined as the amount of feedback loops that are guaranteed to occur if the - selected nodes are placed at the beginning of the order + The lower bound for the number of feedback loops is defined as the amount of feedback loops that are guaranteed + to occur if the current branch is placed at the beginning of the order. The lower bound for the runtime is + defined as the runtime of the current branch. :param branch: the nodes in the branch :type branch: list @@ -1603,6 +1598,7 @@ class RepositoryConnectivityGraph(DataGraph): # Create H-matrix: relation between global design variables and global constraints H = np.array([[float(random.randint(-5, 5)) for _ in range(n_global_var)] for _ in range(n_global_constraints)]) if 'H' not in kwargs else kwargs['H'] + mathematical_problem['H-matrix'] = H # Create I-matrix: relation between local design variables and global constraints I = np.array([[float(random.randint(-5, 5)) for _ in range(sum(n_local_var))] for _ in -- GitLab