From 0f8e35ad4473e8f6192d4ded1fd7729847333a24 Mon Sep 17 00:00:00 2001 From: Anne-Liza <a.m.r.m.bruggeman@student.tudelft.nl> Date: Thu, 23 Nov 2017 13:32:08 +0100 Subject: [PATCH] Added function to sort pre and post coupling functions and changed get_coupling_matrix() such that it accepts a selection of nodes Former-commit-id: a99012bc228329d45851fc836bdbfe0dad1c47b5 --- kadmos/graph/graph_data.py | 66 +++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/kadmos/graph/graph_data.py b/kadmos/graph/graph_data.py index 13e5c2c9e..a94828b44 100644 --- a/kadmos/graph/graph_data.py +++ b/kadmos/graph/graph_data.py @@ -397,16 +397,58 @@ class DataGraph(KadmosGraph): function_graph = nx.contracted_nodes(function_graph, 'super_node', function_id) function_graph.remove_edges_from(function_graph.selfloop_edges()) - # Find function order + # Find a topological function order function_order = nx.topological_sort(function_graph) + + # Get pre-coupling functions and sort + pre_coupling_functions = function_order[:function_order.index('super_node')] + pre_coupling_functions_order = self.sort_non_coupled_nodes(pre_coupling_functions) + + # Sort coupled functions to minimize feedback coupled_functions_order = self.minimize_feedback(coupled_functions, method, multi_start=multi_start) - # Replace super node with coupled functions in function order - function_order[function_order.index('super_node'):function_order.index('super_node') + 1] = \ - coupled_functions_order + # Get post-coupling functions and sort + post_coupling_functions = function_order[function_order.index('super_node') + 1:] + post_coupling_functions_order = self.sort_non_coupled_nodes(post_coupling_functions) + + # Get function_order + function_order = pre_coupling_functions_order + coupled_functions_order + post_coupling_functions_order return function_order + def sort_non_coupled_nodes(self, nodes): + """ Function to sort the pre and post coupling nodes + + :param nodes: nodes that need to be sorted + :type nodes: list + :return nodes in sorted order + :rtype list + """ + + # Check if all nodes are in graph and no feedback loops exists + for func in nodes: + assert func in self, "Function node {} must be present in graph.".format(func) + subgraph = self.get_subgraph_by_function_nodes(nodes) + assert nx.is_directed_acyclic_graph(subgraph) + + # Make sure the nodes are in a topological sort + if self.check_for_coupling(nodes, only_feedback=True): + nodes = nx.topological_sort(subgraph) + + nodes_to_sort = list(nodes) + sorted_nodes = [] + while len(nodes_to_sort) != 0: + # Get the coupling matrix of the nodes that need to be sorted + coupling_matrix = self.get_coupling_matrix(node_selection=nodes_to_sort) + # Find the columns that are zero + idxs = np.where(~coupling_matrix.any(axis=0))[0] + # Add sorted node to sorted list, and delete from to be sorted list + for idx in sorted(idxs, reverse=True): + sorted_nodes.append(nodes_to_sort[idx]) + del nodes_to_sort[idx] + + return sorted_nodes + def minimize_feedback(self, nodes, method, multi_start=None): """Function to find the function order with minimum feedback @@ -2179,17 +2221,22 @@ class FundamentalProblemGraph(DataGraph, KeChainMixin): return - def get_coupling_matrix(self, function_order_method='manual'): + def get_coupling_matrix(self, function_order_method='manual', node_selection=None): """Function to determine the role of the different functions in the FPG. :param function_order_method: algorithm to be used for the order in which the functions are executed. :type function_order_method: basestring + :param node_selection: selection of nodes for which the coupling matrix will be calculated only + :type node_selection: list :return: graph with enriched function node attributes and function problem role dictionary :rtype: FundamentalProblemGraph """ # Make a copy of the graph, check it and remove all inputs and outputs - graph = self.cleancopy() + if node_selection: + graph = self.get_subgraph_by_function_nodes(node_selection) + else: + graph = self.cleancopy() nodes_to_remove = list() # TODO: Consider using the check function assert not graph.find_all_nodes(subcategory='all problematic variables'), 'Graph still has problematic variables.' @@ -2200,8 +2247,11 @@ class FundamentalProblemGraph(DataGraph, KeChainMixin): # Determine and check function ordering method assert function_order_method in self.OPTIONS_FUNCTION_ORDER_METHOD if function_order_method == 'manual': - assert 'function_order' in graph.graph['problem_formulation'], 'function_order must be given as attribute.' - function_order = graph.graph['problem_formulation']['function_order'] + if node_selection: + function_order = node_selection + else: + assert 'function_order' in graph.graph['problem_formulation'], 'function_order must be given as attribute.' + function_order = graph.graph['problem_formulation']['function_order'] elif function_order_method == 'random': function_order = graph.find_all_nodes(category='function') -- GitLab