Source code for cosapp.utils.graph_analysis
from cosapp.systems import System
from cosapp.ports.port import BasePort
from cosapp.core.variableref import VariableReference
from cosapp.utils.naming import natural_varname
from cosapp.utils.helpers import check_arg
from typing import Dict, Set, Tuple
[docs]
def get_free_inputs(system: System) -> Dict[str, VariableReference]:
"""Searches all free inputs within `system`, and returns a dictionary
mapping each input to their source variable (may differ from input,
in case of pulling).
Returns:
--------
mapping: Dict[str, str]
Dictionary of the kind `input_name`: `free_input_name`.
"""
check_arg(system, 'system', System)
def recursive_search(system: System, head: System) -> Tuple[Dict[str, str], Set[str]]:
"""Recursive inner version of `get_dependent_connections`"""
inputs = dict()
bound_inputs = set() # type: Set[str]
prefix = "" if system is head else f"{head.get_path_to_child(system)}."
def get_portname(port: BasePort) -> str:
if port.owner is system:
return f"{prefix}{port.name}" # avoid unnecessary path search
return port.full_name(trim_root=True)
for port in system.inputs.values():
portname = get_portname(port)
inputs.update(
(f"{portname}.{name}", f"{portname}.{name}")
for name in port
)
for connector in system.incoming_connectors():
if not connector.is_active:
continue
sink, source = connector.sink, connector.source
if sink.is_output:
continue
sink_name = get_portname(sink)
if source.is_output:
bound_inputs.update(
f"{sink_name}.{target}"
for target in connector.sink_variables()
)
else:
source_name = get_portname(source)
inputs.update(
(f"{sink_name}.{target}", f"{source_name}.{origin}")
for target, origin in connector.mapping.items()
)
for child in system.children.values():
child_inputs, child_bound_inputs = recursive_search(child, head)
bound_inputs.update(child_bound_inputs)
for key, alias in child_inputs.items():
try:
# Check if `ref` is already mapped in parent
inputs[key] = inputs[alias]
except KeyError:
inputs[key] = alias
return inputs, bound_inputs
root = system.root()
if system is root or system.parent is root:
# Most likely case
start = system
else:
# If system is neither root nor a child of root,
# the search must start just below root, to make sure
# highest-level aliases are not connected to an output.
# This is slow, and should be avoided.
start = system.path()[1]
inputs, bound_inputs = recursive_search(start, root)
bound_inputs &= set(inputs.values())
name2variable = root.name2variable
result = dict()
for key, src in inputs.items():
if src in bound_inputs:
continue
ref = name2variable[key]
try:
context = system.get_path_to_child(ref.context)
except:
# context is not in system tree - skip key
continue
new_key = f"{ref.mapping.name}.{ref.key}"
if context:
new_key = f"{context}.{new_key}"
result[natural_varname(new_key)] = result[new_key] = name2variable[src]
return result