예제 #1
0
def test_topological_generations():
    G = nx.DiGraph(
        {1: [2, 3], 2: [4, 5], 3: [7], 4: [], 5: [6, 7], 6: [], 7: []}
    ).reverse()
    # order within each generation is inconsequential
    generations = [sorted(gen) for gen in nx.topological_generations(G)]
    expected = [[4, 6, 7], [3, 5], [2], [1]]
    assert generations == expected

    MG = nx.MultiDiGraph(G.edges)
    MG.add_edge(2, 1)
    generations = [sorted(gen) for gen in nx.topological_generations(MG)]
    assert generations == expected
def find_groups(
    pjs: Sequence[PredictionJobDataClass], randomize_groups: bool = False
) -> Tuple[nx.DiGraph, List[List[PredictionJobDataClass]]]:
    """Find a sequence of prediction job groups respecting dependencies.

    Compute groups of prediction jobs such that the prediction jobs in a group
    depend of at least one prediction job in the previous group and does not depend
    on a prediction job in the following groups.
    This means that all the prediction jobs in a group can be run in parallel and that
    if groups are treated in the given order, the dependencies of a prediction job have
    already been treated when the prediction job is run.

    Args:
        pjs (Iterable[PredictionJobDataClass]): The sequence of prediction jobs
        randomize_groups (bool, optional): whether subgroups should be randomized.

    Returns:
        nx.Digraph: the dependency graph
        List[List[PredictionJobDataClass]]: The list of prediction job groups
    """
    nodes, edges = build_graph_structure(pjs)
    graph = build_nx_graph(nodes, edges)
    groups = list(nx.topological_generations(graph))

    if randomize_groups:
        for group in groups:
            random.shuffle(group)

    # Convert groups of pj ids to groups of pjs
    pj_id_map = {pj["id"]: i for i, pj in enumerate(pjs)}
    pj_groups = [[pjs[pj_id_map[pj_id]] for pj_id in group] for group in groups]
    return graph, pj_groups
예제 #3
0
def read_packages(variables_fp):
    with open(variables_fp) as fh:
        variables = yaml.load(fh, Loader=yaml.CLoader)

    G = nx.DiGraph()

    for project in variables['projects']:
        G.add_node(project['name'])

    for project in variables['projects']:
        for dep in project.get('deps', []):
            G.add_edge(project['name'], dep)

    return list(reversed(list(nx.topological_generations(G))))
예제 #4
0
    def get_execution_layers(self) -> List[Set[HierarchyPath]]:
        """Get step execution layers, with steps represnted by paths.

        An execution layer is a set of steps that can be executed in
        parallel. The graph's execution layers are an ordered list of
        these layers such that:

        * For a given layer, every step in the layer may be executed as
          soon as all steps in preceding layers have been executed.
        * Every step is in as early a layer as possible.

        In other words, the execution layers are the topological
        generations of the graph, prepended by any sequential steps,
        each in its own layer and in the order in which they were added.

        Returns:
            An ordered list of the execution layers, with each step
            represented by its path.
        """
        layers = nx.topological_generations(self._graph)
        to_return = [set([step]) for step in self._sequential_steps]
        to_return += [set(layer) for layer in layers]
        return to_return
예제 #5
0
def test_topological_generations_cycle():
    G = nx.DiGraph([[2, 1], [3, 1], [1, 2]])
    with pytest.raises(nx.NetworkXUnfeasible):
        list(nx.topological_generations(G))
예제 #6
0
def test_topological_generations_empty():
    G = nx.DiGraph()
    assert list(nx.topological_generations(G)) == []
예제 #7
0
def topological_sort(G):
    """Returns a generator of nodes in topologically sorted order.

    A topological sort is a nonunique permutation of the nodes of a
    directed graph such that an edge from u to v implies that u
    appears before v in the topological sort order. This ordering is
    valid only if the graph has no directed cycles.

    Parameters
    ----------
    G : NetworkX digraph
        A directed acyclic graph (DAG)

    Yields
    ------
    nodes
        Yields the nodes in topological sorted order.

    Raises
    ------
    NetworkXError
        Topological sort is defined for directed graphs only. If the graph `G`
        is undirected, a :exc:`NetworkXError` is raised.

    NetworkXUnfeasible
        If `G` is not a directed acyclic graph (DAG) no topological sort exists
        and a :exc:`NetworkXUnfeasible` exception is raised.  This can also be
        raised if `G` is changed while the returned iterator is being processed

    RuntimeError
        If `G` is changed while the returned iterator is being processed.

    Examples
    --------
    To get the reverse order of the topological sort:

    >>> DG = nx.DiGraph([(1, 2), (2, 3)])
    >>> list(reversed(list(nx.topological_sort(DG))))
    [3, 2, 1]

    If your DiGraph naturally has the edges representing tasks/inputs
    and nodes representing people/processes that initiate tasks, then
    topological_sort is not quite what you need. You will have to change
    the tasks to nodes with dependence reflected by edges. The result is
    a kind of topological sort of the edges. This can be done
    with :func:`networkx.line_graph` as follows:

    >>> list(nx.topological_sort(nx.line_graph(DG)))
    [(1, 2), (2, 3)]

    Notes
    -----
    This algorithm is based on a description and proof in
    "Introduction to Algorithms: A Creative Approach" [1]_ .

    See also
    --------
    is_directed_acyclic_graph, lexicographical_topological_sort

    References
    ----------
    .. [1] Manber, U. (1989).
       *Introduction to Algorithms - A Creative Approach.* Addison-Wesley.
    """
    for generation in nx.topological_generations(G):
        yield from generation
    def plot(self, output: str):
        """
        Visualize a Bayesian Network. Result will be saved
        in parent directory in folder visualization_result.
        output: str name of output file
        """
        if not output.endswith('.html'):
            logger_network.error("This version allows only html format.")
            return None

        G = nx.DiGraph()
        nodes = [node.name for node in self.nodes]
        G.add_nodes_from(nodes)
        G.add_edges_from(self.edges)

        network = Network(height="800px",
                          width="100%",
                          notebook=True,
                          directed=nx.is_directed(G),
                          layout='hierarchical')

        nodes_sorted = np.array(list(nx.topological_generations(G)),
                                dtype=object)

        # Qualitative class of colormaps
        q_classes = [
            'Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2', 'Set1', 'Set2',
            'Set3', 'tab10', 'tab20', 'tab20b', 'tab20c'
        ]

        hex_colors = []
        for cls in q_classes:
            rgb_colors = plt.get_cmap(cls).colors
            hex_colors.extend([
                matplotlib.colors.rgb2hex(rgb_color)
                for rgb_color in rgb_colors
            ])

        hex_colors = np.array(hex_colors)

        # Number_of_colors in matplotlib in Qualitative class = 144

        class_number = len(set([node.type for node in self.nodes]))
        hex_colors_indexes = [
            random.randint(0,
                           len(hex_colors) - 1) for _ in range(class_number)
        ]
        hex_colors_picked = hex_colors[hex_colors_indexes]
        class2color = {
            cls: color
            for cls, color in zip(set([node.type for node in self.nodes]),
                                  hex_colors_picked)
        }
        name2class = {node.name: node.type for node in self.nodes}

        for level in range(len(nodes_sorted)):
            for node_i in range(len(nodes_sorted[level])):
                name = nodes_sorted[level][node_i]
                cls = name2class[name]
                color = class2color[cls]
                network.add_node(name,
                                 label=name,
                                 color=color,
                                 size=45,
                                 level=level,
                                 font={'size': 36},
                                 title=f'Узел байесовской сети {name} ({cls})')

        for edge in G.edges:
            network.add_edge(edge[0], edge[1])

        network.hrepulsion(node_distance=300, central_gravity=0.5)

        if not (os.path.exists('visualization_result')):
            os.mkdir("visualization_result")

        return network.show(f'visualization_result/' + output)