Source code for mmodel.shortcut

"""Shortcuts

The shortcut should work for both the Model and the Experiment classes.
"""

from mmodel.modifier import loop_input, parse_fields, print_output, print_inputs
from mmodel.utility import modelgraph_signature, modelgraph_returns
from networkx.utils import nodes_equal
import networkx as nx






[docs] def loop_shortcut(model, parameter: str, name=None): """Shortcut to add a loop to a subgraph. The parameter needs to be in the graph signature. Otherwise, an exception is raised. For parameters that are not from the graph (signature modified by modifiers), use regular loops or change modifiers. :param model: executable model :param str parameter: loop parameter :param str name: name of the new model, defaults to old model name. :return: a new model with looped parameter """ G = model.graph name = name or model.name modifiers = model.modifiers loop_mod = loop_input(parameter) # If the parameter is in the signature but not in the graph. # This is due to the signature modifier on the model. # Use a regular loop or change the modifiers. if parameter not in modelgraph_signature(G).parameters: raise Exception( f"{repr(parameter)} is not a graph model parameter," f" check parameter input or use a regular loop." ) H = G.subgraph(inputs=[parameter]) # if the nodes are equal, add the loop modifier to the model if nodes_equal(G.nodes, H.nodes): modifiers = [*modifiers, loop_mod] elif len(H.nodes()) == 1: node = list(H.nodes)[0] # if the looped node is only one node # add loop modifier to node attribute node_modifiers = H.nodes[node]["node_object"].modifiers node_modifiers = [*node_modifiers, loop_mod] G = G.edit_node(node, modifiers=node_modifiers) else: # if there is more than one node sub_name = f"subnode_{parameter}" sub_des = ( f"Submodel generated by loop_shortcut for parameter {repr(parameter)}." ) H_returns = modelgraph_returns(H) # empty list if the nodes don't have returns output = ", ".join(H_returns) or None # this is necessary when the subgraph has no returns # create the model and substitute the subgraph looped_node = type(model)( f"submodel_{parameter}", H, model.handler, doc=sub_des ) # use the graph default node type node_cls = G.graph["node_type"] G = G.replace_subgraph( H, node_cls(sub_name, looped_node, output=output, modifiers=[loop_mod]) ) # reset returns to recreate the returns from the subgraphs return model.edit(name=name, graph=G, modifiers=modifiers)