ModuleAlgorithm

# Module Algorithm

Include the Header

Include the Header

ModuleAlgorithm_1ModuleAlgorithmInclude
What is a Module Task

What is a Module Task

ModuleAlgorithm_1WhatIsAModuleTask
Create a Module Task over a Custom Graph

Create a Module Task over a Custom Graph

ModuleAlgorithm_1CreateAModuleTaskOverACustomGraph

Taskflow provides template methods that let users create reusable building blocks called modules. Users can connect modules together to build more complex parallel algorithms.

# Include the Header

You need to include the header file, `taskflow/algorithm/module.hpp`, for creating a module task over a schedulable graph target.

    #include<taskflow/algorithm/module.hpp>

# What is a Module Task

Similar to Composable Tasking, but in a more generic setting, the template function, tf::make_module_task, allows you to create a task over a taskflow graph that can be executed by an executor. This provides a flexible way to encapsulate and reuse complex task logic within your Taskflow applications. The example below demonstrates how to create and launch multiple taskflows in parallel using asynchronous tasking:

    tf::Executorexecutor;

    tf::TaskflowA;
    tf::TaskflowB;
    tf::TaskflowC;
    tf::TaskflowD;

    A.emplace([](){printf("TaskflowA\n");});
    B.emplace([](){printf("TaskflowB\n");});
    C.emplace([](){printf("TaskflowC\n");});
    D.emplace([](){printf("TaskflowD\n");});

    //launchthefourtaskflowsusingasynchronoustasking
    executor.async(tf::make_module_task(A));
    executor.async(tf::make_module_task(B));
    executor.async(tf::make_module_task(C));
    executor.async(tf::make_module_task(D));
    executor.wait_for_all();

Since the four taskflows are launched asynchronously without any dependencies between them, we can observe any order of the output message:

    {.shell-session}
    #onepossibleoutput
    TaskflowB
    TaskflowC
    TaskflowA
    TaskflowD

    #anotherpossibleoutput
    TaskflowD
    TaskflowA
    TaskflowB
    TaskflowC

If you need to enforce dependencies among these four taskflows, you can use dependent-async tasks. The example below launches the four taskflows one by one in sequential:

    tf::Executorexecutor;

    tf::TaskflowA;
    tf::TaskflowB;
    tf::TaskflowC;
    tf::TaskflowD;

    A.emplace([](){printf("TaskflowA\n");});
    B.emplace([](){printf("TaskflowB\n");});
    C.emplace([](){printf("TaskflowC\n");});
    D.emplace([](){printf("TaskflowD\n");});

    autoTA=executor.silent_dependent_async(tf::make_module_task(A));
    autoTB=executor.silent_dependent_async(tf::make_module_task(B),TA);
    autoTC=executor.silent_dependent_async(tf::make_module_task(C),TB);
    auto[TD,FD]=executor.dependent_async(tf::make_module_task(D),TC);
    FD.get();

    {.shell-session}
    #dependent-asynctasksenforceasequentialexecutionofthefourtaskflows
    TaskflowA
    TaskflowB
    TaskflowC
    TaskflowD

The module task maker, tf::make_module_task, functions basically similar to tf::Taskflow::composed_of but provides a more generic interface that can be used beyond Taskflow. Specifically, the following two approaches achieve the same functionality.

    //approach1:compositionusingcomposed_of
    tf::Taskm1=taskflow1.composed_of(taskflow2);

    //approach2:compositionusingmake_module_task
    tf::Taskm1=taskflow1.emplace(tf::make_module_task(taskflow2));

Similar to tf::Taskflow::composed_of, tf::make_module_task does not assume ownership of the provided taskflow but a soft reference. You are responsible for ensuring that the encapsulated taskflow remains valid throughout its execution.

# Create a Module Task over a Custom Graph

In addition to encapsulate taskflow graphs, you can create a module task to schedule a custom graph target. A schedulable target (of type `T`) must define the method `T::graph()` that returns a reference to the tf::Graph object managed by `T`. The following example defines a custom graph that can be scheduled through making module tasks:

    structCustomGraph{
    tf::Graphgraph;
    CustomGraph(){
    //useflowbuildertoinheritalltaskcreationmethodsintf::Taskflow
    tf::FlowBuilderbuilder(graph);
    tf::Tasktask=builder.emplace([](){
    std::cout<<"atask\n";//statictask
    });
    }
    //returnsareferencetothegraphfortaskflowcomposition
    Graph&graph(){returngraph;}
    };

    CustomGraphtarget;
    executor.async(tf::make_module_task(target));

Users are responsible for ensuring the given custom graph remains valid throughout its execution. The executor does not assume ownership of the custom graph.