How to Use GMutable for Dynamic Dependency Management in CGraph

GMutable is a specialized group element in CGraph that enables runtime reshaping of internal dependency graphs through its pure virtual reshape() method, allowing execution paths to adapt dynamically based on runtime data.

The CGraph pipeline framework provides GMutable for scenarios where static dependency definitions are insufficient. Unlike standard groups with fixed topologies, GMutable lets you redefine node connections during pipeline execution, making it ideal for conditional workflows, adaptive algorithms, and data-driven processing chains.

Understanding GMutable Architecture

Inheritance and Core Interface

GMutable extends the standard group functionality through a carefully designed inheritance chain. It derives from GGroup, which in turn inherits from GElement, placing it firmly within CGraph's element hierarchy while adding specialized runtime capabilities.

The base class definition in src/GraphCtrl/GraphElement/GGroup/GMutable/GMutable.h establishes the contract that all mutable groups must fulfill:

class GMutable : public GGroup {
protected:
    /** Redefine the internal data structure */
    virtual CStatus reshape(GElementPtrArr& elements) = 0;
};

The reshape Method Hook

The reshape method serves as the primary extension point for dynamic dependency management. This pure virtual function receives a reference to the group's current element array (GElementPtrArr& elements), allowing you to rewire connections using CGraph's domain-specific language operators.

The method is invoked automatically during pipeline execution. When the scheduler reaches a GMutable instance, it calls GMutable::run(), which internally triggers your overridden reshape implementation before proceeding with the actual node execution.

Implementing Dynamic Dependencies with GMutable

Creating a Custom Mutable Class

To implement dynamic dependency management, you must create a subclass of GMutable and override the reshape method. The implementation in tutorial/MyGMutable/MyMutable.h demonstrates how to switch between four distinct execution patterns based on runtime parameters:

// tutorial/MyGMutable/MyMutable.h
class MyMutable : public CGraph::GMutable {
public:
    CStatus reshape(CGraph::GElementPtrArr& elements) override {
        // Access runtime parameters to determine dependency pattern
        auto param = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, "param1");
        int count = param->iCount % 4;

        if (count == 0) {
            // Pattern 1: Sequential fan-out (a -> [b, c])
            (*elements[0]) --> elements[1] & elements[2];
        } else if (count == 1) {
            // Pattern 2: Repeat and chain (c * 3 -> b -> a)
            (*elements[2]) --*3 > elements[1];
            (*elements[1]) --> elements[0];
        } else if (count == 2) {
            // Pattern 3: Skip intermediate node (a -> c)
            (*elements[0]) --> elements[2];
        } else {
            // Pattern 4: Parallel execution with repetition
            (*elements[0])--;
            (*elements[1]) --*2;
            (*elements[2])--;
        }
        return CStatus();
    }
};

Runtime Dependency Operators

Inside reshape, you manipulate dependencies using CGraph's operator DSL defined in the GElement base class:

  • --> – Establishes a sequential dependency (source must complete before target starts)
  • --*n – Creates a repeat dependency where the source runs n times before triggering the target
  • & – Indicates parallel execution (both targets run concurrently after source completes)
  • -- – Marks an element as runnable without dependencies (used for parallel entry points)

These operators allow you to reconstruct the internal DAG dynamically while maintaining CGraph's type safety and scheduling guarantees.

Registering and Executing Mutable Groups

Pipeline Integration

Mutable groups integrate seamlessly into standard CGraph pipelines through the registerGGroup method. The tutorial file tutorial/T26-Mutable.cpp demonstrates the complete setup:

// tutorial/T26-Mutable.cpp
void tutorial_mutable() {
    // Initialize pipeline
    GPipelinePtr pipeline = GPipelineFactory::create();

    // Create child nodes for the mutable group
    GElementPtr a, b_mutable, c, d = nullptr;
    b_mutable = pipeline->createGGroup<MyMutable>({
        pipeline->createGNode<MyNode1>(GNodeInfo("nodeB1")),
        pipeline->createGNode<MyNode2>(GNodeInfo("nodeB2")),
        pipeline->createGNode<MyNode1>(GNodeInfo("nodeB3"))
    });

    // Register elements with dependencies
    pipeline->registerGElement<MyNode1>(&a, {}, "nodeA", 1);
    pipeline->registerGGroup(&b_mutable, {a}, "mutableB", 1);  // Depends on nodeA
    pipeline->registerGElement<MyNode2>(&c, {a}, "nodeC", 1);
    pipeline->registerGElement<MyWriteParamNode>(&d, {b_mutable, c}, "nodeD", 1);

    // Execute multiple iterations with dynamic reshaping
    pipeline->process(6);

    // Cleanup
    GPipelineFactory::remove(pipeline);
}

Execution Flow and Output

When pipeline->process(6) executes, the mutable group reshapes its internal dependencies six times, potentially producing different execution graphs each iteration. Sample output from the tutorial demonstrates this variability:


---- run as a->[b,c]
---- run as c(*3)->b->a
---- run as a->c, skip b
---- run as [a,b(*2),c]

Each line represents a distinct dependency topology created inside reshape based on the current value of the runtime parameter. The rest of the pipeline (nodes A, C, and D) maintains its static dependencies while the mutable group B adapts internally.

Summary

  • GMutable is an abstract group class in CGraph that enables runtime dependency graph modification through the pure virtual reshape method defined in src/GraphCtrl/GraphElement/GGroup/GMutable/GMutable.h.
  • The reshape method receives a reference to the group's element array, allowing you to rewire dependencies using CGraph's DSL operators (-->, --*, &, etc.) based on runtime conditions.
  • Mutable groups integrate seamlessly into standard pipelines via registerGGroup and trigger reshaping automatically during each execution cycle, as demonstrated in tutorial/T26-Mutable.cpp.
  • This architecture supports dynamic pipelines where execution paths adapt to data, parameters, or external signals without requiring static graph reconstruction.

Frequently Asked Questions

What is the difference between GMutable and static GGroup?

GMutable requires implementation of the reshape method and recalculates internal dependencies during every execution cycle, while static GGroup subclasses (like GRegion or GCondition) maintain fixed topologies defined at pipeline construction time. Use GMutable when your workflow needs to adapt based on runtime data, and static groups when the execution order remains constant.

When is the reshape method called during execution?

The reshape method is invoked automatically by GMutable::run() each time the pipeline scheduler reaches the mutable group during pipeline->process(). This occurs before any child elements execute, ensuring the dependency graph is reconstructed fresh for every iteration based on current runtime parameters or state.

Can GMutable groups be nested inside other groups?

Yes, GMutable instances can be nested within other group types (including other GMutable groups) because they inherit from GGroup, which is a standard GElement. When nesting mutables, each level invokes its own reshape method independently during execution, allowing for hierarchical dynamic dependency management where parent groups control high-level flow and nested mutables handle fine-grained adaptation.

How do I pass runtime parameters to reshape logic?

Access runtime parameters inside reshape using the CGRAPH_GET_GPARAM_WITH_NO_EMPTY macro (or similar parameter accessors) to retrieve shared state objects. As shown in tutorial/MyGMutable/MyMutable.h, you can read parameter values at the start of reshape and use conditional logic to select different dependency wiring patterns based on the current data, enabling truly data-driven dynamic graphs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →