Source code for mrfmsim.shortcut

"""Shortcuts

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

from mmodel.modifier import loop_input
from mmodel.utility import modelgraph_signature, modelgraph_returns
from mrfmsim import Node
from networkx.utils import nodes_equal
from mrfmsim.modifier import parse_fields, print_output, print_inputs
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 (generated 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 """ # check if the parameter is in the signature if parameter not in model.signature.parameters: raise Exception(f"Invalid shortcut: {repr(parameter)} is not a model input.") G = model.graph name = name or model.name modifiers = model.modifiers components = model.components 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 regular loop or change the modifiers. if parameter not in modelgraph_signature(G).parameters: raise Exception( f"{repr(parameter)} is not included in the graph." f" Use regular loop or change the modifier." ) H = G.subgraph(inputs=[parameter]) 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 # if not H_returns: # output = None # else: # output = ", ".join(H_returns) output = ", ".join(H_returns) # create the model and substitute the subgraph looped_node = type(model)( f"submodel_{parameter}", H, model.handler, doc=sub_des ) G = G.replace_subgraph( H, Node(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, components=components)