Групповые задачи для параллельной обработки в направленном ациклическом графе зависимостей с использованием топологической сортировки

У меня есть класс Task, который зависит от других задач перед выполнением. Я хочу сгруппировать задачи, которые можно распараллелить, и упорядочить их. Я решил, что сначала его можно представить как DAG, и пытаюсь использовать JGrapht. Сначала я просматриваю входной список задач, чтобы получить все задачи с их зависимостями и собрать их в один список. Затем для каждой задачи я создаю вершину в графе.

DirectedAcyclicGraph<Task, DefaultEdge> d = new DirectedAcyclicGraph<>(DefaultEdge.class);
Set<Task> all = collectAllTasks(tasks);
all.forEach(d::addVertex);

затем, используя тот же список, я пытаюсь создать грани между узлами.

all.forEach(task -> {
        Set<TaskName> predecessors = task.getPredecessors();

        predecessors.forEach(name -> {
            Task predecessor = taskService.getTaskByName(name);
            d.addEdge(predecessor, task);
        });

    });

Потом пытаюсь сгруппировать задачи

private Set<Set<TaskId>> groupTasks(DirectedAcyclicGraph<TaskId, DefaultEdge> dag) {
    Set<Set<TaskId>> groups = new LinkedHashSet<>();
    Iterator<TaskId> iterator = new TopologicalOrderIterator<>(dag);

    iterator.forEachRemaining(taskId -> {
        //source?
        if (dag.incomingEdgesOf(taskId).isEmpty()) {
            if (groups.isEmpty()) {
                Set<TaskId> set = new HashSet<>();
                set.add(taskId);
                groups.add(set);
            } else {
                groups.iterator().next().add(taskId);
            }
        }

        Set<TaskId> tasks = new HashSet<>(Graphs.successorListOf(dag, taskId));

        //sink?
        if (tasks.isEmpty()) {
            return;
        }

        groups.add(featuresGroup);
    });

    return groups;
}

Таким образом, результат - это упорядоченные и сгруппированные задачи, например для графа

введите описание изображения здесь

Результат будет A, B, {C, D}, E.

Однако он полностью нарушает тот случай, когда B также является предшественником E, как в этом примере.

введите описание изображения здесь

Как я могу добиться порядка A, B, {C, D}, E для графика, подобного предыдущему? Есть ли какой-то конкретный алгоритм, который я могу найти, или как я могу добиться этого лучше? Спасибо.


person Johny Placebo    schedule 30.04.2021    source источник
comment
Выглядит очень похоже на эту топологическую сортировку stackoverflow.com/questions/67265652/, посмотрите   -  person Rocco    schedule 30.04.2021
comment
Попробуйте сделать заголовок более информативным. Что-то вроде: Групповые задачи для параллельной обработки в направленном ациклическом графе зависимостей с использованием топологической сортировки. Так другим пользователям будет легче найти ваш вопрос.   -  person Joris Kinable    schedule 01.05.2021
comment
Отвечает ли это на ваш вопрос? Выполнение направленного ациклического графа задач параллельно   -  person Anmol Singh Jaggi    schedule 08.05.2021


Ответы (1)


Решение можно получить, используя следующую процедуру:

  1. Первая группа содержит все задачи без входящих дуг: эти задачи не имеют входящих зависимостей.
  2. Удалите с графика все задачи из первой группы.
  3. Во вторую группу входят задачи без входящих дуг в модифицированном графе: все зависимости для этих задач выполнены.
  4. Удалите из измененного графа все задачи из второй группы. Продолжайте этот процесс, пока все задачи не будут удалены.

Используя JGraphT:

public static List<List<String>> getGroups(Graph<String, DefaultEdge> taskGraph){
    List<List<String>> groups = new ArrayList<>();
    //The first group contains all vertices without incoming arcs
    List<String> group = new LinkedList<>();
    for(String task : taskGraph.vertexSet())
        if(taskGraph.inDegreeOf(task) == 0)
            group.add(task);
    //Next we construct all remaining groups. The group k+1 consists of al vertices without incoming arcs if we were
    //to remove all vertices in the previous group k from the graph.
    do {
        groups.add(group);
        List<String> nextGroup = new LinkedList<>();
        for (String task : group) {
            for (String nextTask : Graphs.neighborSetOf(taskGraph, task)) {
                if (taskGraph.inDegreeOf(nextTask) == 1)
                    nextGroup.add(nextTask);
            }
            taskGraph.removeVertex(task); //Removes a vertex and all its edges from the graph
        }
        group=nextGroup;
    }while(!group.isEmpty());
    return groups;
}
public static Graph<String, DefaultEdge> getGraph1(){
    Graph<String, DefaultEdge> taskGraph=new SimpleDirectedGraph<>(DefaultEdge.class);
    Graphs.addAllVertices(taskGraph, Arrays.asList("A","B","C","D","E"));
    taskGraph.addEdge("A","B");
    taskGraph.addEdge("B","C");
    taskGraph.addEdge("B","D");
    taskGraph.addEdge("C","E");
    taskGraph.addEdge("D","E");
    return taskGraph;
}
public static Graph<String, DefaultEdge> getGraph2(){
    Graph<String, DefaultEdge> taskGraph=new SimpleDirectedGraph<>(DefaultEdge.class);
    Graphs.addAllVertices(taskGraph, Arrays.asList("A","B","C","D","E"));
    taskGraph.addEdge("A","B");
    taskGraph.addEdge("B","C");
    taskGraph.addEdge("B","D");
    taskGraph.addEdge("B","E");
    taskGraph.addEdge("C","E");
    taskGraph.addEdge("D","E");
    return taskGraph;
}
public static void main(String[] args){
    System.out.println("Graph1: "+getGroups(getGraph1()));
    System.out.println("Graph2: "+getGroups(getGraph2()));
}

Вывод:

Graph1: [[A], [B], [C, D], [E]]
Graph2: [[A], [B], [C, D], [E]]

Примечание: приведенный выше код предполагает, что входной граф действительно является допустимым графом задачи. Вы можете встроить дополнительную проверку для выявления циклических зависимостей, например если бы у вас была последовательность вроде: A - ›B -› A.

person Joris Kinable    schedule 30.04.2021
comment
Спасибо. В этом есть смысл. - person Johny Placebo; 01.05.2021