Пример #1
0
def test_merge_nodes():
    Graph = graph_from_edges(("A", "B", "C"), ("B", "D"))

    mGraph = merge_nodes(Graph, {"B": "A"})
    assert set(mGraph.nodes) == {"A", "C", "D"}
    assert set(mGraph.edges) == {("A", "C"), ("A", "D")}
    # check Graph wasn't modified
    assert set(Graph.nodes) == {"A", "B", "C", "D"}

    mGraph = merge_nodes(Graph, {"C": "B", "D": "B"})
    assert set(mGraph.nodes) == {"A", "B"}
    assert set(mGraph.edges) == {('A', 'B')}
Пример #2
0
def _rec_convert_graph_to_code(Graph,
                               all_models_params,
                               models_dico,
                               model_name_mapping=None,
                               composition_already_done=None):
    """ recursive function used to convert a Graph into a json code 
   
    See convert_graph_to_code
    """

    if composition_already_done is None:
        composition_already_done = set()

    if len(Graph.nodes) == 1:
        node = list(Graph.nodes)[0]
        return models_dico[node]

    node = _find_first_composition_node(Graph, composition_already_done)

    if node is not None:
        successors = list(Graph.successors(node))
        assert len(successors) > 0

    else:
        successors = []

    if node is None or len(successors) == 0:
        ### ** It's means I'll return a GraphPipeline ** ###
        # 2 cases :
        # * nodes is None  : meaning there is no composition node

        if len(successors) > 0:
            raise ValueError(
                "a composition node should have at most one successor '%s'" %
                str(node))

        # assert len(successors) > 0

        # it shouldn't append ...
        # 1) either it an original node => composition node => no successor isn't possible
        # 2) the node was already handled => should have been in the list

        edges = gh.edges_from_graph(Graph)

        if model_name_mapping is None:
            model_name_mapping = _create_name_mapping(list(Graph.nodes))
        # each node in graph will be mapped to a name within the GraphPipeline

        models = {model_name_mapping[n]: models_dico[n] for n in Graph.nodes}

        edges = [
            tuple((model_name_mapping[e] for e in edge)) for edge in edges
        ]

        return (SpecialModels.GraphPipeline, {
            "models": models,
            "edges": edges
        })

    composition_already_done.add(node)  # to prevent looping on the same node

    all_sub_branch_nodes = {}
    all_terminal_nodes = []
    for successor in successors:

        sub_branch_nodes = list(
            gh.subbranch_search(starting_node=successor,
                                Graph=Graph,
                                visited={node}))

        all_sub_branch_nodes[successor] = sub_branch_nodes

        assert successor in sub_branch_nodes

        sub_Graph = Graph.subgraph(sub_branch_nodes)

        all_terminal_nodes += gh.get_terminal_nodes(sub_Graph)

        models_dico[successor] = _rec_convert_graph_to_code(
            sub_Graph,
            all_models_params=all_models_params,
            models_dico=models_dico,
            model_name_mapping=model_name_mapping,
            composition_already_done=composition_already_done,
        )

    # Check
    all_s = [
        frozenset(Graph.successors(t_node)) for t_node in all_terminal_nodes
    ]
    if len(set(all_s)) != 1:
        # By convention, if we look at the nodes AFTER the composition
        # (ie : the successors of the terminal nodes of the part of the graph that will be merged by the composition)
        # Those nodes should have the same list of successors. Those successors will be the successors of the merged node
        raise ValueError(
            "The successor at the end of the composition node %s are not always the same"
            % str(node))

    if len(successors) == 1:

        # Only one sucessor of composition node

        models_dico[node] = (_klass_from_node(node),
                             models_dico[successors[0]],
                             all_models_params[node])

    elif len(successors) > 1:

        models_dico[node] = (
            _klass_from_node(node),
            [models_dico[successor] for successor in successors],
            all_models_params[node],
        )

    else:
        raise NotImplementedError("can't go there")

    # Now I need to merge 'node' with all the sub-branches
    nodes_mapping = {}
    for successor, sub_branch_nodes in all_sub_branch_nodes.items():
        for n in sub_branch_nodes:
            nodes_mapping[n] = node

    Gmerged = gh.merge_nodes(Graph, nodes_mapping=nodes_mapping)
    # All the node in successor will be 'fused' with 'node' ...
    # Recurse now, that the composition node is taken care of

    return _rec_convert_graph_to_code(
        Gmerged,
        all_models_params=all_models_params,
        models_dico=models_dico,
        model_name_mapping=model_name_mapping,
        composition_already_done=composition_already_done,
    )