---
title: anyflow_pretrain
---

<div class="python-reference">
<a id="fastvideo.train.methods.distribution_matching.anyflow_pretrain-classes"></a>
<a id="fastvideo.train.methods.distribution_matching.anyflow_pretrain-functions"></a>
<a id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod-functions"></a>
<a id="fastvideo.train.methods.distribution_matching.anyflow_pretrain"></a>
<p>AnyFlow pretrain (flow-map central-difference) training method.</p>
<p>Stage 1 of the AnyFlow two-stage recipe. Trains a single student network
<code>u_θ(x_t, t, r)</code> to predict the average velocity from time <code>t</code> back
to time <code>r</code> via the central-difference target</p>
<pre><code>target = (eps - x_0) - ((t - r) / N) * dF/dt
</code></pre>
<p>where <code>N = num_train_timesteps</code> and <code>dF/dt</code> is estimated from the
student's own forward at <code>(t ± δ, r)</code> (with one-sided fallback near the
schedule endpoints).</p>
<p>Per-batch <code>(t, r)</code> sampling follows the AnyFlow paper:</p>
<ul>
<li><code>diffusion_ratio</code> fraction: <code>r = t</code> (recovers plain flow matching).</li>
<li><code>consistency_ratio</code> fraction: <code>r = 0</code> (consistency to clean data).</li>
<li>Remaining fraction: <code>(t, r) = (max, min)</code> of two independent uniform
  draws (full reconstruction range).</li>
</ul>
<p>Reference: <code>trainer_wan_anyflow_pretrain.py</code> in NVlabs/AnyFlow at
commit <code>549236a</code>.</p>
<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod</code></h2>
<pre class="doc-signature"><code>TrainingMethod(*, cfg: Any, role_models: dict&#91;str, ModelBase&#93;)</code></pre>
<p>Base training method (algorithm layer).</p>
<p>Subclasses own their role models (student, teacher, critic, …) as
plain attributes and manage optimizers directly — no <code>RoleManager</code>
or <code>RoleHandle</code>.</p>
<p>The constructor receives <em>role_models</em> (a <code>dict[str, ModelBase]</code>)
and a <em>cfg</em> object.  It calls <code>init_preprocessors</code> on the student
and builds <code>self.role_modules</code> for FSDP wrapping.</p>
<p>A single shared CUDA RNG generator (<code>cuda_generator</code>) is
created in :meth:<code>on_train_start</code>.  All <code>torch.randn</code> /
<code>torch.randint</code> calls in methods <strong>and</strong> models must use this
generator instead of relying on global RNG state.</p>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_tracker"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_tracker</code></h3>
<pre class="doc-signature"><code>set_tracker(tracker: Any) -&gt; None</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.single_train_step"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.single_train_step</code></h3>
<pre class="doc-signature"><code>single_train_step(batch: dict&#91;str, Any&#93;, iteration: int) -&gt; tuple&#91;dict&#91;str, torch.Tensor&#93;, dict&#91;str, Any&#93;, dict&#91;str, LogScalar&#93;&#93;</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_optimizers"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_optimizers</code></h3>
<pre class="doc-signature"><code>get_optimizers(iteration: int) -&gt; Sequence&#91;torch.optim.Optimizer&#93;</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_lr_schedulers"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_lr_schedulers</code></h3>
<pre class="doc-signature"><code>get_lr_schedulers(iteration: int) -&gt; Sequence&#91;Any&#93;</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.checkpoint_state"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.checkpoint_state</code></h3>
<pre class="doc-signature"><code>checkpoint_state() -&gt; dict&#91;str, Any&#93;</code></pre>
<p>Return DCP-ready checkpoint state for all trainable roles.</p>
<p>Keys follow the convention:
<code>roles.&lt;role&gt;.&lt;module&gt;</code>, <code>optimizers.&lt;role&gt;</code>,
<code>schedulers.&lt;role&gt;</code>, <code>random_state.*</code>.</p>
<p>EMA state is managed by the <code>EMACallback</code> and is
checkpointed through the callback state mechanism.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.backward"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.backward</code></h3>
<pre class="doc-signature"><code>backward(loss_map: dict&#91;str, torch.Tensor&#93;, outputs: dict&#91;str, Any&#93;, *, grad_accum_rounds: int = 1) -&gt; None</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.optimizers_schedulers_step"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.optimizers_schedulers_step</code></h3>
<pre class="doc-signature"><code>optimizers_schedulers_step(iteration: int) -&gt; None</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.optimizers_zero_grad"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.optimizers_zero_grad</code></h3>
<pre class="doc-signature"><code>optimizers_zero_grad(iteration: int) -&gt; None</code></pre>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.seed_optimizer_state_for_resume"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.seed_optimizer_state_for_resume</code></h3>
<pre class="doc-signature"><code>seed_optimizer_state_for_resume() -&gt; None</code></pre>
<p>Seed optimizer state so DCP can load saved state.</p>
<p>A fresh optimizer has empty state (exp_avg, exp_avg_sq,
step are only created on the first optimizer.step()).
DCP needs matching entries to load into; without them
the saved optimizer state is silently dropped.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.manages_optimization"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.manages_optimization</code></h3>
<pre class="doc-signature"><code>manages_optimization() -&gt; bool</code></pre>
<p>Whether the method owns backward/optimizer stepping internally.</p>
<p>Most methods return loss tensors and let :class:<code>Trainer</code> handle
gradient accumulation, callbacks, optimizer stepping, and scheduler
stepping. RL-style methods such as DiffusionNFT need to preserve their
own sample-then-inner-train loop, so they can provide a specific
<code>managed_train_step</code>.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.managed_train_step"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.managed_train_step</code></h3>
<pre class="doc-signature"><code>managed_train_step(data_stream: Any, iteration: int) -&gt; tuple&#91;dict&#91;str, torch.Tensor&#93;, dict&#91;str, Any&#93;, dict&#91;str, LogScalar&#93;&#93;</code></pre>
<p>Run one method-managed step.</p>
<p>Subclasses that return <code>True</code> from :meth:<code>manages_optimization</code>
should override this. The fallback consumes one dataloader batch and
delegates to <code>single_train_step</code> so tests can exercise the hook with
tiny fake methods.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.on_validation_begin"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.on_validation_begin</code></h3>
<pre class="doc-signature"><code>on_validation_begin(iteration: int = 0) -&gt; dict&#91;str, LogScalar&#93;</code></pre>
<p>Run method-owned validation, if any.</p>
<p>Pipeline-style validation should remain in callbacks. Methods that
intentionally avoid inference pipelines, such as RL methods with their
own sampler/reward loop, can override this hook and return metrics for
the trainer to log at <code>iteration</code>.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_grad_clip_targets"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_grad_clip_targets</code></h3>
<pre class="doc-signature"><code>get_grad_clip_targets(iteration: int) -&gt; dict&#91;str, torch.nn.Module&#93;</code></pre>
<p>Return modules whose gradients should be clipped.</p>
<p>Override in subclasses to add/conditionally include
modules (e.g. critic, conditionally student).
Default: student transformer.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.apply_configured_lrs"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.apply_configured_lrs</code></h3>
<pre class="doc-signature"><code>apply_configured_lrs() -&gt; None</code></pre>
<p>Re-apply the config's learning rates to live optimizers/schedulers.</p>
<p>Called after a checkpoint resume when
<code>training.checkpoint.reset_lr_on_resume</code> is set: DCP restores
param-group <code>lr</code>/<code>initial_lr</code> and scheduler <code>base_lrs</code> from the
checkpoint, which would otherwise silently override an LR change in
the YAML. Methods with per-role LRs override this.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.inference_checkpoint_modules"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.inference_checkpoint_modules</code></h3>
<pre class="doc-signature"><code>inference_checkpoint_modules(role: str = &#x27;student&#x27;) -&gt; dict&#91;str, torch.nn.Module&#93;</code></pre>
<p>Return the complete modules that form an inference checkpoint.</p>
<p>This intentionally does not reuse :meth:<code>checkpoint_state</code>: resumable
state includes optimizers and every trainable role, while inference
exports select one explicit role and must retain frozen parameters too.
Methods with a different deployable role contract may override this
hook; EMA is never selected implicitly.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.inference_checkpoint_base_model_path"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.inference_checkpoint_base_model_path</code></h3>
<pre class="doc-signature"><code>inference_checkpoint_base_model_path(role: str = &#x27;student&#x27;) -&gt; str</code></pre>
<p>Return the immutable model directory supplying non-trained parts.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_grad_sanitize_targets"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.get_grad_sanitize_targets</code></h3>
<pre class="doc-signature"><code>get_grad_sanitize_targets(iteration: int) -&gt; dict&#91;str, torch.nn.Module&#93;</code></pre>
<p>Return active modules whose gradients must be made finite.</p>
<p>Most methods sanitize and clip the same modules. Methods that leave
an optimizer role unclipped can override this independently.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_is_last_backward"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_is_last_backward</code></h3>
<pre class="doc-signature"><code>set_is_last_backward(iteration: int, is_last_backward: bool) -&gt; None</code></pre>
<p>Mark an FSDP2 accumulation boundary without changing reductions.</p>
<p>Intermediate backwards still reduce-scatter their gradients. Only
FSDP's pending-reduction waits and backward-prefetch cleanup are
deferred. Use the active gradient targets, including roles whose
gradients are sanitized but deliberately not clipped. Non-FSDP
modules are unchanged.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_requires_gradient_sync"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.set_requires_gradient_sync</code></h3>
<pre class="doc-signature"><code>set_requires_gradient_sync(iteration: int, requires_gradient_sync: bool) -&gt; None</code></pre>
<p>Control FSDP gradient communication for accumulation rounds.</p>
<p>FSDP2 normally reduces gradients after every backward. During
gradient accumulation only the final microbatch needs that
communication; earlier rounds can accumulate local gradients. The
active gradient targets include roles that are deliberately not clipped.</p>
<p>Non-FSDP modules do not expose <code>set_requires_gradient_sync</code> and are
intentionally left unchanged.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.supports_gradient_accumulation_no_sync"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.supports_gradient_accumulation_no_sync</code></h3>
<pre class="doc-signature"><code>supports_gradient_accumulation_no_sync(iteration: int) -&gt; bool</code></pre>
<p>Whether every active role safely retains unsharded gradients.</p>
<p>FSDP2 no-sync holds full, unreduced gradients between accumulation
rounds. Model plugins must opt in explicitly; this prevents the
optimization from adding roughly one full model's FP32 gradients to
memory for ordinary full-weight training.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.on_train_start"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.TrainingMethod.on_train_start</code></h3>
<pre class="doc-signature"><code>on_train_start() -&gt; None</code></pre>

<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase</code></h2>
<pre class="doc-signature"><code>ModelBase(*, trainable: bool = True, lora: LoraConfig | dict&#91;str, Any&#93; | None = None, attention_backend: AttentionBackendEnum | str | None = None)</code></pre>
<p>Per-role model instance.</p>
<p>Every role (student, teacher, critic, …) gets its own <code>ModelBase</code>
instance.  Each instance owns its own <code>transformer</code> and
<code>noise_scheduler</code>.  Heavyweight resources (VAE, dataloader, RNG
seeds) are loaded lazily via :meth:<code>init_preprocessors</code>, which the
method calls <strong>only on the student</strong>.</p>

<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.attention_backend_name"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.attention_backend_name</code></h3>
<pre class="doc-signature"><code>attention_backend_name: str | None</code></pre>
<p>Explicit per-role backend name, or <code>None</code> for global/default.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.transformer_module_type"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.transformer_module_type</code></h3>
<pre class="doc-signature"><code>transformer_module_type: str</code></pre>
<p>Physical checkpoint component that owns this role's transformer.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.device"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.device</code></h3>
<pre class="doc-signature"><code>device: torch.device</code></pre>
<p>The local CUDA device for this rank.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.init_preprocessors"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.init_preprocessors</code></h3>
<pre class="doc-signature"><code>init_preprocessors(training_config: TrainingConfig) -&gt; None</code></pre>
<p>Load VAE, build dataloader, seed RNGs.</p>
<p>Called only on the student by the method's <code>__init__</code>.
Default is a no-op so teacher/critic instances skip this.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.on_train_start"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.on_train_start</code></h3>
<pre class="doc-signature"><code>on_train_start() -&gt; None</code></pre>
<p>Called once before the training loop begins.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.eager_optimizer_state_parameters"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.eager_optimizer_state_parameters</code></h3>
<pre class="doc-signature"><code>eager_optimizer_state_parameters() -&gt; tuple&#91;torch.nn.Parameter, ...&#93;</code></pre>
<p>Parameters whose optimizer state must exist before their first grad.</p>
<p>Most models return an empty tuple. Models with intermittently used
trainable parameters can override this so strict distributed
checkpoints retain a stable optimizer-state schema across steps.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.decode_latents"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.decode_latents</code></h3>
<pre class="doc-signature"><code>decode_latents(latents_b_t_c_h_w: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Decode <code>[B, T, C, H, W]</code> latents to <code>[B, C, T, H, W]</code> media.</p>
<p>RL reward methods call this hook instead of reaching into
model-specific VAE normalization details.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.num_train_timesteps"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.num_train_timesteps</code></h3>
<pre class="doc-signature"><code>num_train_timesteps: int</code></pre>
<p>Return the scheduler's training timestep horizon.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.shift_and_clamp_timestep"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.shift_and_clamp_timestep</code></h3>
<pre class="doc-signature"><code>shift_and_clamp_timestep(timestep: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Apply model/pipeline timestep shifting and clamp.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.prepare_batch"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.prepare_batch</code></h3>
<pre class="doc-signature"><code>prepare_batch(raw_batch: dict&#91;str, Any&#93;, *, generator: torch.Generator, latents_source: Literal&#91;&#x27;data&#x27;, &#x27;zeros&#x27;&#93; = &#x27;data&#x27;) -&gt; TrainingBatch</code></pre>
<p>Convert a dataloader batch into forward primitives.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.add_noise"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.add_noise</code></h3>
<pre class="doc-signature"><code>add_noise(clean_latents: torch.Tensor, noise: torch.Tensor, timestep: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Apply forward-process noise at <em>timestep</em>.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.add_noise_for_batch"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.add_noise_for_batch</code></h3>
<pre class="doc-signature"><code>add_noise_for_batch(clean_latents: torch.Tensor, noise: torch.Tensor, timestep: torch.Tensor, batch: TrainingBatch) -&gt; torch.Tensor</code></pre>
<p>Apply noise with optional immutable geometry carried by <code>batch</code>.</p>
<p>Video-only and fixed-shape models keep their existing behavior. Joint
packed models override this hook when splitting the tensor requires
batch-local shape context.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.predict_noise"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.predict_noise</code></h3>
<pre class="doc-signature"><code>predict_noise(noisy_latents: torch.Tensor, timestep: torch.Tensor, batch: TrainingBatch, *, conditional: bool, cfg_uncond: dict&#91;str, Any&#93; | None = None, attn_kind: Literal&#91;&#x27;dense&#x27;, &#x27;vsa&#x27;&#93; = &#x27;dense&#x27;) -&gt; NoisePrediction</code></pre>
<p>Predict video flow or an ordered <code>(video, audio)</code> flow pair.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.predict_x0"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.predict_x0</code></h3>
<pre class="doc-signature"><code>predict_x0(noisy_latents: torch.Tensor, timestep: torch.Tensor, batch: TrainingBatch, *, conditional: bool, cfg_uncond: dict&#91;str, Any&#93; | None = None, attn_kind: Literal&#91;&#x27;dense&#x27;, &#x27;vsa&#x27;&#93; = &#x27;dense&#x27;) -&gt; torch.Tensor</code></pre>
<p>Convert a video-only flow prediction to clean video latents.</p>
<p>This helper owns one noisy video tensor and one video scheduler. Joint
video/audio callers apply their modality-specific conversions where
both noisy tensors and both schedulers are available.</p>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.backward"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.ModelBase.backward</code></h3>
<pre class="doc-signature"><code>backward(loss: torch.Tensor, ctx: Any, *, grad_accum_rounds: int) -&gt; None</code></pre>
<p>Backward that may restore forward-context.</p>
<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.get_optional_float"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.get_optional_float</code></h2>
<pre class="doc-signature"><code>get_optional_float(mapping: dict&#91;str, Any&#93;, key: str, *, where: str) -&gt; float | None</code></pre>

<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.get_optional_int"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.get_optional_int</code></h2>
<pre class="doc-signature"><code>get_optional_int(mapping: dict&#91;str, Any&#93;, key: str, *, where: str) -&gt; int | None</code></pre>

<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.build_optimizer_and_scheduler"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.build_optimizer_and_scheduler</code></h2>
<pre class="doc-signature"><code>build_optimizer_and_scheduler(*, params: list&#91;torch.nn.Parameter&#93;, optimizer_config: OptimizerConfig, loop_config: TrainingLoopConfig, learning_rate: float, betas: tuple&#91;float, float&#93;, scheduler_name: str, weight_decay: float | None = None) -&gt; tuple&#91;torch.optim.Optimizer, object&#93;</code></pre>
<p>Build an AdamW optimizer and LR scheduler.</p>
<p>Returns <code>(optimizer, lr_scheduler)</code> so the caller can store them
as method-level attributes.</p>
<h2 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod</code></h2>
<pre class="doc-signature"><code>AnyFlowPretrainMethod(*, cfg: Any, role_models: dict&#91;str, ModelBase&#93;)</code></pre>
<p>AnyFlow flow-map pretrain method.</p>
<p>Single-student training; no teacher or critic. The student must
implement <code>predict_velocity_with_r(noisy, t, r, batch, ...)</code> —
typically a <code>WanModel</code> with <code>r_embedder=True</code> in its arch config.</p>

<details><summary>Source</summary><pre><code>class AnyFlowPretrainMethod(TrainingMethod):
    &quot;&quot;&quot;AnyFlow flow-map pretrain method.

    Single-student training; no teacher or critic. The student must
    implement ``predict_velocity_with_r(noisy, t, r, batch, ...)`` —
    typically a ``WanModel`` with ``r_embedder=True`` in its arch config.
    &quot;&quot;&quot;

    def __init__(
        self,
        *,
        cfg: Any,
        role_models: dict&#91;str, ModelBase&#93;,
    ) -&gt; None:
        super().__init__(cfg=cfg, role_models=role_models)
        if &quot;student&quot; not in role_models:
            raise ValueError(&quot;AnyFlowPretrainMethod requires role &#x27;student&#x27;&quot;)
        if not self.student._trainable:
            raise ValueError(&quot;AnyFlowPretrainMethod requires student to be trainable&quot;)

        mcfg = self.method_config
        self._diffusion_ratio = float(
            get_optional_float(mcfg, &quot;diffusion_ratio&quot;, where=&quot;method.diffusion_ratio&quot;) or 0.5)
        self._consistency_ratio = float(
            get_optional_float(mcfg, &quot;consistency_ratio&quot;, where=&quot;method.consistency_ratio&quot;) or 0.25)
        if self._diffusion_ratio + self._consistency_ratio &gt; 1.0:
            raise ValueError(&quot;method.diffusion_ratio + method.consistency_ratio must &quot;
                             f&quot;be &lt;= 1, got &#123;self._diffusion_ratio&#125; + &quot;
                             f&quot;&#123;self._consistency_ratio&#125;&quot;)

        # δ: finite-difference step in absolute train-timestep units.
        epsilon = get_optional_int(mcfg, &quot;epsilon&quot;, where=&quot;method.epsilon&quot;)
        self._fd_epsilon = float(epsilon) if epsilon is not None else 5.0

        # Loss weighting scheme (uniform / gaussian / beta08).
        raw_weight_type = mcfg.get(&quot;weight_type&quot;, &quot;beta08&quot;)
        if not isinstance(raw_weight_type, str):
            raise ValueError(&quot;method.weight_type must be a string, got &quot;
                             f&quot;&#123;type(raw_weight_type).__name__&#125;&quot;)
        weight_type = raw_weight_type.strip().lower()
        if weight_type not in &#123;&quot;uniform&quot;, &quot;gaussian&quot;, &quot;beta08&quot;&#125;:
            raise ValueError(&quot;method.weight_type must be one of &quot;
                             &quot;&#123;uniform, gaussian, beta08&#125;, &quot;
                             f&quot;got &#123;raw_weight_type!r&#125;&quot;)
        self._weight_type = weight_type

        # Guidance fused into the training target (default 1.0 = unused).
        fg = get_optional_float(mcfg, &quot;fuse_guidance_scale&quot;, where=&quot;method.fuse_guidance_scale&quot;)
        self._fuse_guidance_scale = float(fg) if fg is not None else 1.0
        if self._fuse_guidance_scale &lt;= 0.0:
            raise ValueError(&quot;method.fuse_guidance_scale must be positive, &quot;
                             f&quot;got &#123;self._fuse_guidance_scale&#125;&quot;)

        # Flow-map scheduler — uses pipeline_config.flow_shift if present
        # and falls back to method.shift (and finally 1.0).
        shift = float(getattr(self.training_config.pipeline_config, &quot;flow_shift&quot;, 0.0) or 0.0)
        if shift &lt;= 0.0:
            shift_override = get_optional_float(mcfg, &quot;shift&quot;, where=&quot;method.shift&quot;)
            shift = float(shift_override) if shift_override is not None else 1.0
        self._shift = shift

        # Lazy-imported to avoid circular imports on package load.
        from fastvideo.models.schedulers.scheduling_flow_map_euler_discrete import (
            FlowMapEulerDiscreteScheduler, )
        self._flow_map_scheduler = FlowMapEulerDiscreteScheduler(
            num_train_timesteps=int(self.student.num_train_timesteps),
            shift=self._shift,
        )

        self.student.init_preprocessors(self.training_config)
        self._init_optimizer_and_scheduler()

    @property
    def _optimizer_dict(self) -&gt; dict&#91;str, torch.optim.Optimizer&#93;:
        return &#123;&quot;student&quot;: self._student_optimizer&#125;

    @property
    def _lr_scheduler_dict(self) -&gt; dict&#91;str, Any&#93;:
        return &#123;&quot;student&quot;: self._student_lr_scheduler&#125;

    def get_optimizers(
        self,
        iteration: int,
    ) -&gt; Sequence&#91;torch.optim.Optimizer&#93;:
        del iteration
        return &#91;self._student_optimizer&#93;

    def get_lr_schedulers(self, iteration: int) -&gt; Sequence&#91;Any&#93;:
        del iteration
        return &#91;self._student_lr_scheduler&#93;

    def single_train_step(
        self,
        batch: dict&#91;str, Any&#93;,
        iteration: int,
    ) -&gt; tuple&#91;
            dict&#91;str, torch.Tensor&#93;,
            dict&#91;str, Any&#93;,
            dict&#91;str, LogScalar&#93;,
    &#93;:
        del iteration  # AnyFlow pretrain has no iteration-dependent dispatch.

        training_batch = self.student.prepare_batch(
            batch,
            generator=self.cuda_generator,
            latents_source=&quot;data&quot;,
        )
        latents = training_batch.latents  # &#91;B, T, C, H, W&#93; (post-permute in prepare_batch).
        if latents is None or latents.ndim != 5:
            raise RuntimeError(&quot;AnyFlow pretrain expects TrainingBatch.latents of shape &quot;
                               &quot;&#91;B, T, C, H, W&#93; after prepare_batch; got &quot;
                               f&quot;&#123;None if latents is None else tuple(latents.shape)&#125;&quot;)
        device = latents.device
        dtype = latents.dtype
        batch_size = int(latents.shape&#91;0&#93;)

        # AnyFlow (t, r) sampling — overrides the timestep drawn by
        # WanModel._sample_timesteps inside prepare_batch.
        t_norm, r_norm, is_diffusion, is_consistency = _sample_pair_timesteps(
            batch_size=batch_size,
            diffusion_ratio=self._diffusion_ratio,
            consistency_ratio=self._consistency_ratio,
            device=device,
            generator=self.cuda_generator,
        )

        sched = self._flow_map_scheduler
        n_train = float(self.student.num_train_timesteps)
        t = (sched.apply_shift(t_norm) * n_train).to(device=device, dtype=dtype)
        r = (sched.apply_shift(r_norm) * n_train).to(device=device, dtype=dtype)

        # Fresh noise drawn from the method&#x27;s RNG; ignore the noise that
        # prepare_batch attached (it pairs with the discarded timestep).
        noise = torch.randn(
            latents.shape,
            device=device,
            dtype=dtype,
            generator=self.cuda_generator,
        )
        noisy = sched.add_noise(latents, noise, t)

        # Keep training_batch coherent with the new (t, noisy): downstream
        # forward_context uses these fields.
        training_batch.timesteps = t
        training_batch.noise = noise
        training_batch.noisy_model_input = noisy.permute(0, 2, 1, 3, 4)

        # Student velocity prediction at (t, r).
        noise_pred = self.student.predict_velocity_with_r(
            noisy,
            t,
            r,
            training_batch,
            conditional=True,
            attn_kind=&quot;dense&quot;,
        )

        # Optional guidance distillation — fuse CFG into the training target so
        # the resulting checkpoint can be sampled at guidance_scale=1.0.
        if self._fuse_guidance_scale != 1.0:
            with torch.no_grad():
                noise_pred_uncond = self.student.predict_velocity_with_r(
                    noisy,
                    t,
                    r,
                    training_batch,
                    conditional=False,
                    attn_kind=&quot;dense&quot;,
                )
            g = float(self._fuse_guidance_scale)
            noise_pred = (noise_pred - (1.0 - g) * noise_pred_uncond) / g

        dF_dt = _central_difference_dF_dt(
            student=self.student,
            batch=training_batch,
            noisy=noisy,
            latents=latents,
            noise=noise,
            t=t,
            r=r,
            delta=self._fd_epsilon,
            num_train_timesteps=n_train,
            attn_kind=&quot;dense&quot;,
            guidance_scale=self._fuse_guidance_scale,
        )

        # AnyFlow target: target = (eps - x_0) - (t - r) * dF/dt
        # dF/dt is in (velocity per absolute t-unit); (t - r) is in absolute units;
        # the product cancels back to velocity units, matching noise_pred.
        view = &#91;batch_size&#93; + &#91;1&#93; * (latents.ndim - 1)
        target = (noise - latents) - (t - r).view(*view) * dF_dt

        # Per-sample squared error, then per-timestep weight, then scale-balance
        # so the non-diffusion branches stay on the same magnitude as the
        # diffusion branch (matches AnyFlow&#x27;s stop-grad rescaling).
        per_sample = torch.mean(
            ((noise_pred.float() - target.float())**2).reshape(batch_size, -1),
            dim=-1,
        )
        weight = sched.get_train_weight(t, weight_type=self._weight_type)
        per_sample = per_sample * weight

        with torch.no_grad():
            diff_mask = is_diffusion
            diff_mean = per_sample&#91;diff_mask&#93;.mean() if diff_mask.any() else per_sample.mean()
            non_diff_mask = ~diff_mask
            if non_diff_mask.any():
                scale = diff_mean / (per_sample&#91;non_diff_mask&#93; + 1e-5)
            else:
                scale = torch.tensor(1.0, device=device, dtype=per_sample.dtype)
        non_diff_idx = torch.nonzero(non_diff_mask, as_tuple=False).flatten()
        if non_diff_idx.numel() &gt; 0:
            per_sample = per_sample.clone()
            per_sample&#91;non_diff_idx&#93; = per_sample&#91;non_diff_idx&#93; * scale

        total_loss = per_sample.mean()

        loss_map = &#123;&quot;total_loss&quot;: total_loss&#125;
        metrics: dict&#91;str, LogScalar&#93; = &#123;
            &quot;diffusion_fraction&quot;: float(is_diffusion.float().mean()),
            &quot;consistency_fraction&quot;: float(is_consistency.float().mean()),
            &quot;scale_weight_mean&quot;: float(scale.mean()) if isinstance(scale, torch.Tensor) else float(scale),
        &#125;
        outputs = &#123;
            &quot;student_ctx&quot;: (
                training_batch.timesteps,
                training_batch.attn_metadata,
            ),
        &#125;
        return loss_map, outputs, metrics

    def backward(
        self,
        loss_map: dict&#91;str, torch.Tensor&#93;,
        outputs: dict&#91;str, Any&#93;,
        *,
        grad_accum_rounds: int = 1,
    ) -&gt; None:
        &quot;&quot;&quot;Route the loss backward through the student&#x27;s forward_context
        so attn metadata stays attached during gradient computation.&quot;&quot;&quot;
        student_ctx = outputs.get(&quot;student_ctx&quot;)
        if student_ctx is None:
            super().backward(loss_map, outputs, grad_accum_rounds=grad_accum_rounds)
            return
        self.student.backward(
            loss_map&#91;&quot;total_loss&quot;&#93;,
            student_ctx,
            grad_accum_rounds=grad_accum_rounds,
        )

    def _init_optimizer_and_scheduler(self) -&gt; None:
        tc = self.training_config
        params = &#91;p for p in self.student.transformer.parameters() if p.requires_grad&#93;
        (
            self._student_optimizer,
            self._student_lr_scheduler,
        ) = build_optimizer_and_scheduler(
            params=params,
            optimizer_config=tc.optimizer,
            loop_config=tc.loop,
            learning_rate=float(tc.optimizer.learning_rate),
            betas=tc.optimizer.betas,
            scheduler_name=str(tc.optimizer.lr_scheduler),
        )</code></pre></details>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.get_optimizers"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.get_optimizers</code></h3>
<pre class="doc-signature"><code>get_optimizers(iteration: int) -&gt; Sequence&#91;torch.optim.Optimizer&#93;</code></pre>

<details><summary>Source</summary><pre><code>def get_optimizers(
    self,
    iteration: int,
) -&gt; Sequence&#91;torch.optim.Optimizer&#93;:
    del iteration
    return &#91;self._student_optimizer&#93;</code></pre></details>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.get_lr_schedulers"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.get_lr_schedulers</code></h3>
<pre class="doc-signature"><code>get_lr_schedulers(iteration: int) -&gt; Sequence&#91;Any&#93;</code></pre>

<details><summary>Source</summary><pre><code>def get_lr_schedulers(self, iteration: int) -&gt; Sequence&#91;Any&#93;:
    del iteration
    return &#91;self._student_lr_scheduler&#93;</code></pre></details>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.single_train_step"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.single_train_step</code></h3>
<pre class="doc-signature"><code>single_train_step(batch: dict&#91;str, Any&#93;, iteration: int) -&gt; tuple&#91;dict&#91;str, torch.Tensor&#93;, dict&#91;str, Any&#93;, dict&#91;str, LogScalar&#93;&#93;</code></pre>

<details><summary>Source</summary><pre><code>def single_train_step(
    self,
    batch: dict&#91;str, Any&#93;,
    iteration: int,
) -&gt; tuple&#91;
        dict&#91;str, torch.Tensor&#93;,
        dict&#91;str, Any&#93;,
        dict&#91;str, LogScalar&#93;,
&#93;:
    del iteration  # AnyFlow pretrain has no iteration-dependent dispatch.

    training_batch = self.student.prepare_batch(
        batch,
        generator=self.cuda_generator,
        latents_source=&quot;data&quot;,
    )
    latents = training_batch.latents  # &#91;B, T, C, H, W&#93; (post-permute in prepare_batch).
    if latents is None or latents.ndim != 5:
        raise RuntimeError(&quot;AnyFlow pretrain expects TrainingBatch.latents of shape &quot;
                           &quot;&#91;B, T, C, H, W&#93; after prepare_batch; got &quot;
                           f&quot;&#123;None if latents is None else tuple(latents.shape)&#125;&quot;)
    device = latents.device
    dtype = latents.dtype
    batch_size = int(latents.shape&#91;0&#93;)

    # AnyFlow (t, r) sampling — overrides the timestep drawn by
    # WanModel._sample_timesteps inside prepare_batch.
    t_norm, r_norm, is_diffusion, is_consistency = _sample_pair_timesteps(
        batch_size=batch_size,
        diffusion_ratio=self._diffusion_ratio,
        consistency_ratio=self._consistency_ratio,
        device=device,
        generator=self.cuda_generator,
    )

    sched = self._flow_map_scheduler
    n_train = float(self.student.num_train_timesteps)
    t = (sched.apply_shift(t_norm) * n_train).to(device=device, dtype=dtype)
    r = (sched.apply_shift(r_norm) * n_train).to(device=device, dtype=dtype)

    # Fresh noise drawn from the method&#x27;s RNG; ignore the noise that
    # prepare_batch attached (it pairs with the discarded timestep).
    noise = torch.randn(
        latents.shape,
        device=device,
        dtype=dtype,
        generator=self.cuda_generator,
    )
    noisy = sched.add_noise(latents, noise, t)

    # Keep training_batch coherent with the new (t, noisy): downstream
    # forward_context uses these fields.
    training_batch.timesteps = t
    training_batch.noise = noise
    training_batch.noisy_model_input = noisy.permute(0, 2, 1, 3, 4)

    # Student velocity prediction at (t, r).
    noise_pred = self.student.predict_velocity_with_r(
        noisy,
        t,
        r,
        training_batch,
        conditional=True,
        attn_kind=&quot;dense&quot;,
    )

    # Optional guidance distillation — fuse CFG into the training target so
    # the resulting checkpoint can be sampled at guidance_scale=1.0.
    if self._fuse_guidance_scale != 1.0:
        with torch.no_grad():
            noise_pred_uncond = self.student.predict_velocity_with_r(
                noisy,
                t,
                r,
                training_batch,
                conditional=False,
                attn_kind=&quot;dense&quot;,
            )
        g = float(self._fuse_guidance_scale)
        noise_pred = (noise_pred - (1.0 - g) * noise_pred_uncond) / g

    dF_dt = _central_difference_dF_dt(
        student=self.student,
        batch=training_batch,
        noisy=noisy,
        latents=latents,
        noise=noise,
        t=t,
        r=r,
        delta=self._fd_epsilon,
        num_train_timesteps=n_train,
        attn_kind=&quot;dense&quot;,
        guidance_scale=self._fuse_guidance_scale,
    )

    # AnyFlow target: target = (eps - x_0) - (t - r) * dF/dt
    # dF/dt is in (velocity per absolute t-unit); (t - r) is in absolute units;
    # the product cancels back to velocity units, matching noise_pred.
    view = &#91;batch_size&#93; + &#91;1&#93; * (latents.ndim - 1)
    target = (noise - latents) - (t - r).view(*view) * dF_dt

    # Per-sample squared error, then per-timestep weight, then scale-balance
    # so the non-diffusion branches stay on the same magnitude as the
    # diffusion branch (matches AnyFlow&#x27;s stop-grad rescaling).
    per_sample = torch.mean(
        ((noise_pred.float() - target.float())**2).reshape(batch_size, -1),
        dim=-1,
    )
    weight = sched.get_train_weight(t, weight_type=self._weight_type)
    per_sample = per_sample * weight

    with torch.no_grad():
        diff_mask = is_diffusion
        diff_mean = per_sample&#91;diff_mask&#93;.mean() if diff_mask.any() else per_sample.mean()
        non_diff_mask = ~diff_mask
        if non_diff_mask.any():
            scale = diff_mean / (per_sample&#91;non_diff_mask&#93; + 1e-5)
        else:
            scale = torch.tensor(1.0, device=device, dtype=per_sample.dtype)
    non_diff_idx = torch.nonzero(non_diff_mask, as_tuple=False).flatten()
    if non_diff_idx.numel() &gt; 0:
        per_sample = per_sample.clone()
        per_sample&#91;non_diff_idx&#93; = per_sample&#91;non_diff_idx&#93; * scale

    total_loss = per_sample.mean()

    loss_map = &#123;&quot;total_loss&quot;: total_loss&#125;
    metrics: dict&#91;str, LogScalar&#93; = &#123;
        &quot;diffusion_fraction&quot;: float(is_diffusion.float().mean()),
        &quot;consistency_fraction&quot;: float(is_consistency.float().mean()),
        &quot;scale_weight_mean&quot;: float(scale.mean()) if isinstance(scale, torch.Tensor) else float(scale),
    &#125;
    outputs = &#123;
        &quot;student_ctx&quot;: (
            training_batch.timesteps,
            training_batch.attn_metadata,
        ),
    &#125;
    return loss_map, outputs, metrics</code></pre></details>
<h3 id="fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.backward"><code>fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod.backward</code></h3>
<pre class="doc-signature"><code>backward(loss_map: dict&#91;str, torch.Tensor&#93;, outputs: dict&#91;str, Any&#93;, *, grad_accum_rounds: int = 1) -&gt; None</code></pre>
<p>Route the loss backward through the student's forward_context
so attn metadata stays attached during gradient computation.</p>
<details><summary>Source</summary><pre><code>def backward(
    self,
    loss_map: dict&#91;str, torch.Tensor&#93;,
    outputs: dict&#91;str, Any&#93;,
    *,
    grad_accum_rounds: int = 1,
) -&gt; None:
    &quot;&quot;&quot;Route the loss backward through the student&#x27;s forward_context
    so attn metadata stays attached during gradient computation.&quot;&quot;&quot;
    student_ctx = outputs.get(&quot;student_ctx&quot;)
    if student_ctx is None:
        super().backward(loss_map, outputs, grad_accum_rounds=grad_accum_rounds)
        return
    self.student.backward(
        loss_map&#91;&quot;total_loss&quot;&#93;,
        student_ctx,
        grad_accum_rounds=grad_accum_rounds,
    )</code></pre></details>
</div>
