Diffusers documentation

LoopSequentialPipelineBlocks

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.34.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

LoopSequentialPipelineBlocks

LoopSequentialPipelineBlocks are a multi-block type that composes other ModularPipelineBlocks together in a loop. Data flows circularly, using intermediate_inputs and intermediate_outputs, and each block is run iteratively. This is typically used to create a denoising loop which is iterative by default.

This guide shows you how to create LoopSequentialPipelineBlocks.

Loop wrapper

LoopSequentialPipelineBlocks, is also known as the loop wrapper because it defines the loop structure, iteration variables, and configuration. Within the loop wrapper, you need the following variables.

  • loop_inputs are user provided values and equivalent to inputs.
  • loop_intermediate_inputs are intermediate variables from the PipelineState and equivalent to ~modular_pipelines.ModularPipelineBlocks.intermediate_inputs.
  • loop_intermediate_outputs are new intermediate variables created by the block and added to the PipelineState. It is equivalent to intermediate_outputs.
  • __call__ method defines the loop structure and iteration logic.
import torch
from diffusers.modular_pipelines import LoopSequentialPipelineBlocks, ModularPipelineBlocks, InputParam, OutputParam

class LoopWrapper(LoopSequentialPipelineBlocks):
    model_name = "test"
    @property
    def description(self):
        return "I'm a loop!!"
    @property
    def loop_inputs(self):
        return [InputParam(name="num_steps")]
    @torch.no_grad()
    def __call__(self, components, state):
        block_state = self.get_block_state(state)
        # Loop structure - can be customized to your needs
        for i in range(block_state.num_steps):
            # loop_step executes all registered blocks in sequence
            components, block_state = self.loop_step(components, block_state, i=i)
        self.set_block_state(state, block_state)
        return components, state

The loop wrapper can pass additional arguments, like current iteration index, to the loop blocks.

Loop blocks

A loop block is a ModularPipelineBlocks, but the __call__ method behaves differently.

  • It recieves the iteration variable from the loop wrapper.
  • It works directly with the BlockState instead of the PipelineState.
  • It doesn’t require retrieving or updating the BlockState.

Loop blocks share the same BlockState to allow values to accumulate and change for each iteration in the loop.

class LoopBlock(ModularPipelineBlocks):
    model_name = "test"
    @property
    def inputs(self):
        return [InputParam(name="x")]
    @property
    def intermediate_outputs(self):
        # outputs produced by this block
        return [OutputParam(name="x")]
    @property
    def description(self):
        return "I'm a block used inside the `LoopWrapper` class"
    def __call__(self, components, block_state, i: int):
        block_state.x += 1
        return components, block_state

LoopSequentialPipelineBlocks

Use the from_blocks_dict() method to add the loop block to the loop wrapper to create LoopSequentialPipelineBlocks.

loop = LoopWrapper.from_blocks_dict({"block1": LoopBlock})

Add more loop blocks to run within each iteration with from_blocks_dict(). This allows you to modify the blocks without changing the loop logic itself.

loop = LoopWrapper.from_blocks_dict({"block1": LoopBlock(), "block2": LoopBlock})
< > Update on GitHub