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 the modifiers. :param model: executable model :param str parameter: loop parameter :param str name: name of the new model; defaults to the old model name. :return: a new model with the 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)