diff --git a/ProConPy/csp_solver.py b/ProConPy/csp_solver.py index 05f525b..552e2cd 100644 --- a/ProConPy/csp_solver.py +++ b/ProConPy/csp_solver.py @@ -566,30 +566,29 @@ def _refresh_options_validities(self, var): The variable whose assignment triggers the refresh of the options validities of other variables. """ - # Set of variables that have been visited - visited = set() - # Queue of variables to be visited queue = [neig for neig in self._cgraph[var] if neig.has_options()] + # Set of all variables that have been queued + queued = set(queue) + # Traverse the constraint graph to refresh the options validities of all possibly affected variables while queue: - # Pop the first variable from the queue and mark it as visited + # Pop the first variable from the queue var = queue.pop(0) logger.debug("Refreshing options validities of %s.", var) - visited.add(var) - # Update the validities of the variable and extend the list of variables to refresh + # Update the validities of the variable and extend the queue if necessary validities_changed = var.update_options_validities() if validities_changed: - queue.extend( - [ - neig - for neig in self._cgraph[var] - if neig.has_options() and neig not in visited - ] - ) + extension = [ + neig + for neig in self._cgraph[var] + if neig.has_options() and neig not in queued + ] + queue.extend(extension) + queued.update(extension) def apply_assignment_assertions(self, solver, exclude_var=None, exclude_vars=None): """Apply the assignment assertions to the given solver. The assignment assertions are diff --git a/tools/cgraph_plotter.py b/tools/cgraph_plotter.py index ad695dc..de99542 100644 --- a/tools/cgraph_plotter.py +++ b/tools/cgraph_plotter.py @@ -11,8 +11,13 @@ import networkx as nx import matplotlib.pyplot as plt from networkx.drawing.nx_pydot import graphviz_layout +import os +light_color = "sandybrown" +dark_color = "sienna" +highlight_color = "tomato" + def initialize(cime): """Initializes visualCaseGen""" ConfigVar.reboot() @@ -45,31 +50,79 @@ def plot_cgraph(): G = gen_cgraph() pos = graphviz_layout(G, prog="sfdp") + plt.figure(figsize=(10, 5)) + nx.draw( G, pos, with_labels=False, node_size=100, - node_color="skyblue", + node_color=light_color, font_color="black", edge_color="gray", linewidths=0.5, width=0.5, - alpha=0.5, + alpha=0.7, ) text = nx.draw_networkx_labels(G, pos) for _, t in text.items(): # t.set_rotation(20) # t.set_verticalalignment("center_baseline") - t.set_fontsize(8) + t.set_fontsize(9) - plt.show() + #plt.show() + plt.savefig('cgraph.png') +def animate_graph_traversal(traversal_list): + """Animates the graph traversal and saves the frames as png files.""" + G = gen_cgraph() + pos = graphviz_layout(G, prog="sfdp") + fig, ax = plt.subplots() + frames = [] + + plt.figure(figsize=(10, 5)) + + for i, node in enumerate(traversal_list): + nx.draw( + G, + pos, + with_labels=False, + node_size=100, + node_color=light_color, + font_color="black", + edge_color="gray", + linewidths=0.5, + width=0.5, + alpha=0.7, + ) + visited_nodes = set(traversal_list[:i+1]) + text = nx.draw_networkx_labels(G, pos, labels={node: node if node in visited_nodes else "" for node in G.nodes()}) + for _, t in text.items(): + t.set_fontsize(9) + t.set_verticalalignment("bottom") + + # Dark color for already visited nodes + nx.draw_networkx_nodes(G, pos, nodelist=visited_nodes, node_color=dark_color, node_size=100) + + # Highlight the current node being traversed + nx.draw_networkx_nodes(G, pos, nodelist=[node], node_color=highlight_color, node_size=100) + + # Highlight the edges in the current path + #if i > 0: + # for j in range(i): + # nx.draw_networkx_edges(G, pos, edgelist=[(traversal_list[j], traversal_list[j+1])], edge_color="red", width=2) + + # Save the frame as a png file + frame_path = f"frame_{i}.png" + frames.append(frame_path) + plt.savefig(frame_path) + plt.clf() def main(): initialize(CIME_interface()) plot_cgraph() + animate_graph_traversal([cvars["COMP_OCN"], cvars["COMP_ATM"], cvars["COMP_LND"], cvars["COMP_ICE"], cvars['COMP_OCN_PHYS'], cvars['COMP_OCN_OPTION']]) if __name__ == "__main__":