---
title: scheduling_flow_map_euler_discrete
---

<div class="python-reference">
<a id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete-classes"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler-functions"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler--parameters"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete"></a>
<p>Flow-map any-step Euler scheduler for AnyFlow.</p>
<p>The model predicts the <em>average</em> velocity <code>u_θ(x_t, t, r)</code> from time
<code>t</code> back to time <code>r</code>, so one Euler step is</p>
<pre><code>x_r = x_t - ((t - r) / num_train_timesteps) * u_θ(x_t, t, r)
</code></pre>
<p>regardless of how far apart <code>t</code> and <code>r</code> are. The scheduler also
provides the AnyFlow training-time helpers <code>apply_shift</code> (flow-matching
shift transform) and <code>get_train_weight</code> (per-timestep loss weight,
including <code>beta08</code>).</p>
<p>Standalone — does not depend on diffusers' ConfigMixin/SchedulerMixin.</p>
<h2 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler</code></h2>
<pre class="doc-signature"><code>BaseScheduler(*args, **kwargs)</code></pre>


<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.set_shift"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.set_shift</code></h3>
<pre class="doc-signature"><code>set_shift(shift: float) -&gt; None</code></pre>

<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.set_timesteps"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.set_timesteps</code></h3>
<pre class="doc-signature"><code>set_timesteps(*args, **kwargs) -&gt; None</code></pre>

<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.scale_model_input"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.BaseScheduler.scale_model_input</code></h3>
<pre class="doc-signature"><code>scale_model_input(sample: torch.Tensor, timestep: int | None = None) -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler</code></h2>
<pre class="doc-signature"><code>FlowMapEulerDiscreteScheduler(*, num_train_timesteps: int = 1000, shift: float = 1.0)</code></pre>
<p>Minimal flow-map scheduler.</p>
<h2>Parameters</h2>
<p>num_train_timesteps:
    Discretization granularity for training. <code>t</code> is expressed in
    absolute units in <code>[0, num_train_timesteps]</code>.
shift:
    Flow-matching shift parameter (Wan video default: <code>5.0</code>). Set
    to <code>1.0</code> for an identity shift.</p>

<details><summary>Source</summary><pre><code>class FlowMapEulerDiscreteScheduler(BaseScheduler):
    &quot;&quot;&quot;Minimal flow-map scheduler.

    Parameters
    ----------
    num_train_timesteps:
        Discretization granularity for training. ``t`` is expressed in
        absolute units in ``&#91;0, num_train_timesteps&#93;``.
    shift:
        Flow-matching shift parameter (Wan video default: ``5.0``). Set
        to ``1.0`` for an identity shift.
    &quot;&quot;&quot;

    order: int = 1

    def __init__(
        self,
        *,
        num_train_timesteps: int = 1000,
        shift: float = 1.0,
    ) -&gt; None:
        self.num_train_timesteps = int(num_train_timesteps)
        self.shift = float(shift)
        self.timesteps: torch.Tensor = torch.empty(0)
        self.sigmas: torch.Tensor = torch.empty(0)
        super().__init__()

    # ------------------------------------------------------------------
    # BaseScheduler abstract surface

    def set_shift(self, shift: float) -&gt; None:
        self.shift = float(shift)

    def scale_model_input(
        self,
        sample: torch.Tensor,
        timestep: int | None = None,
    ) -&gt; torch.Tensor:
        # Flow-matching has no per-step input scaling; pass through.
        del timestep
        return sample

    # ------------------------------------------------------------------
    # Public helpers used by the AnyFlow pretrain method.

    def apply_shift(
        self,
        t: torch.Tensor,
        *,
        shift: float | None = None,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;Apply the flow-matching shift: ``t&#x27; = s * t / (1 + (s - 1) * t)``.

        Operates in the normalized ``&#91;0, 1&#93;`` domain — callers should pass
        ``t / num_train_timesteps`` (or sample ``t`` directly from
        ``&#91;0, 1&#93;``).
        &quot;&quot;&quot;
        s = self.shift if shift is None else float(shift)
        if s == 1.0:
            return t
        return s * t / (1.0 + (s - 1.0) * t)

    def get_train_weight(
        self,
        t: torch.Tensor,
        *,
        weight_type: WeightType = &quot;beta08&quot;,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;Per-timestep training weight, renormalized so the total weight
        mass equals ``num_train_timesteps`` (matching AnyFlow reference&#x27;s
        ``scheduling_flowmap_euler_discrete.py``).

        ``beta08``: ``w(t) = t * sqrt(1 - t)`` (in normalized t-space).
        &quot;&quot;&quot;
        # Auto-detect domain: if t was given in absolute units, normalize.
        t_f = t.float()
        max_val = t_f.max() if t_f.numel() &gt; 0 else torch.tensor(0.0)
        if max_val &gt; 1.0 + 1e-6:
            t_norm = t_f / self.num_train_timesteps
        else:
            t_norm = t_f
        t_norm = t_norm.clamp(min=0.0, max=1.0)

        if weight_type == &quot;uniform&quot;:
            w = torch.ones_like(t_norm)
        elif weight_type == &quot;gaussian&quot;:
            w = torch.exp(-0.5 * ((t_norm - 0.5) / 0.2)**2)
        elif weight_type == &quot;beta08&quot;:
            w = t_norm.pow(1.0) * (1.0 - t_norm).clamp_min(0.0).pow(0.5)
        else:
            raise ValueError(f&quot;Unknown weight_type: &#123;weight_type!r&#125;&quot;)

        denom = w.sum().clamp_min(1e-8)
        return w * (float(self.num_train_timesteps) / denom)

    # ------------------------------------------------------------------

    def set_timesteps(
        self,
        *,
        num_inference_steps: int,
        device: torch.device | str = &quot;cpu&quot;,
        custom_timesteps: list&#91;float&#93; | torch.Tensor | None = None,
    ) -&gt; None:
        &quot;&quot;&quot;Build a descending timestep schedule ending at 0.

        With ``num_inference_steps=N`` the schedule has ``N + 1`` entries
        ``&#91;T_max, ..., 0&#93;`` so a rollout consumes ``N`` Euler steps.

        ``custom_timesteps`` overrides the linspace+shift schedule with
        a pinned list (in absolute train-timestep units), useful for the
        AnyFlow paper&#x27;s hand-tuned ``&#91;999, 937, 833, 624, 0&#93;`` schedule.
        &quot;&quot;&quot;
        if num_inference_steps &lt;= 0:
            raise ValueError(&quot;num_inference_steps must be positive, &quot;
                             f&quot;got &#123;num_inference_steps&#125;&quot;)
        device = torch.device(device)

        if custom_timesteps is not None:
            ts = torch.as_tensor(custom_timesteps, dtype=torch.float32, device=device)
            if ts.ndim != 1:
                raise ValueError(&quot;custom_timesteps must be 1-D, got shape &quot;
                                 f&quot;&#123;tuple(ts.shape)&#125;&quot;)
            if not torch.all(ts&#91;:-1&#93; &gt;= ts&#91;1:&#93;):
                raise ValueError(&quot;custom_timesteps must be descending (largest first)&quot;)
        else:
            ts_norm = torch.linspace(1.0, 0.0, num_inference_steps + 1, device=device)
            ts_norm = self.apply_shift(ts_norm)
            ts = ts_norm * self.num_train_timesteps

        self.timesteps = ts
        self.sigmas = ts / self.num_train_timesteps

    def step(
        self,
        model_output: torch.Tensor,
        *,
        sample: torch.Tensor,
        timestep: torch.Tensor,
        r_timestep: torch.Tensor,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;One Euler step from ``t`` to ``r``.

        ``model_output`` is the average-velocity prediction
        ``u_θ(x_t, t, r)``. Both ``timestep`` and ``r_timestep`` are in
        absolute train-timestep units (``&#91;0, num_train_timesteps&#93;``).
        &quot;&quot;&quot;
        t = timestep.to(sample.device, dtype=sample.dtype)
        r = r_timestep.to(sample.device, dtype=sample.dtype)
        dt_norm = (t - r) / float(self.num_train_timesteps)
        # Broadcast dt over channel/spatial dims.
        view: list&#91;int&#93; = &#91;-1&#93; + &#91;1&#93; * (sample.ndim - 1)
        return sample - dt_norm.view(*view) * model_output

    def add_noise(
        self,
        original_samples: torch.Tensor,
        noise: torch.Tensor,
        timestep: torch.Tensor,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;Linear flow-matching interpolation: ``x_t = (1 - σ) * x_0 + σ * ε``,
        where ``σ = t / num_train_timesteps``.
        &quot;&quot;&quot;
        sigma = (timestep.to(original_samples.device, dtype=original_samples.dtype) / float(self.num_train_timesteps))
        view: list&#91;int&#93; = &#91;-1&#93; + &#91;1&#93; * (original_samples.ndim - 1)
        sigma = sigma.view(*view)
        return (1.0 - sigma) * original_samples + sigma * noise</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.set_shift"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.set_shift</code></h3>
<pre class="doc-signature"><code>set_shift(shift: float) -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def set_shift(self, shift: float) -&gt; None:
    self.shift = float(shift)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.scale_model_input"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.scale_model_input</code></h3>
<pre class="doc-signature"><code>scale_model_input(sample: torch.Tensor, timestep: int | None = None) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def scale_model_input(
    self,
    sample: torch.Tensor,
    timestep: int | None = None,
) -&gt; torch.Tensor:
    # Flow-matching has no per-step input scaling; pass through.
    del timestep
    return sample</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.apply_shift"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.apply_shift</code></h3>
<pre class="doc-signature"><code>apply_shift(t: torch.Tensor, *, shift: float | None = None) -&gt; torch.Tensor</code></pre>
<p>Apply the flow-matching shift: <code>t' = s * t / (1 + (s - 1) * t)</code>.</p>
<p>Operates in the normalized <code>[0, 1]</code> domain — callers should pass
<code>t / num_train_timesteps</code> (or sample <code>t</code> directly from
<code>[0, 1]</code>).</p>
<details><summary>Source</summary><pre><code>def apply_shift(
    self,
    t: torch.Tensor,
    *,
    shift: float | None = None,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Apply the flow-matching shift: ``t&#x27; = s * t / (1 + (s - 1) * t)``.

    Operates in the normalized ``&#91;0, 1&#93;`` domain — callers should pass
    ``t / num_train_timesteps`` (or sample ``t`` directly from
    ``&#91;0, 1&#93;``).
    &quot;&quot;&quot;
    s = self.shift if shift is None else float(shift)
    if s == 1.0:
        return t
    return s * t / (1.0 + (s - 1.0) * t)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.get_train_weight"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.get_train_weight</code></h3>
<pre class="doc-signature"><code>get_train_weight(t: torch.Tensor, *, weight_type: WeightType = &#x27;beta08&#x27;) -&gt; torch.Tensor</code></pre>
<p>Per-timestep training weight, renormalized so the total weight
mass equals <code>num_train_timesteps</code> (matching AnyFlow reference's
<code>scheduling_flowmap_euler_discrete.py</code>).</p>
<p><code>beta08</code>: <code>w(t) = t * sqrt(1 - t)</code> (in normalized t-space).</p>
<details><summary>Source</summary><pre><code>def get_train_weight(
    self,
    t: torch.Tensor,
    *,
    weight_type: WeightType = &quot;beta08&quot;,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Per-timestep training weight, renormalized so the total weight
    mass equals ``num_train_timesteps`` (matching AnyFlow reference&#x27;s
    ``scheduling_flowmap_euler_discrete.py``).

    ``beta08``: ``w(t) = t * sqrt(1 - t)`` (in normalized t-space).
    &quot;&quot;&quot;
    # Auto-detect domain: if t was given in absolute units, normalize.
    t_f = t.float()
    max_val = t_f.max() if t_f.numel() &gt; 0 else torch.tensor(0.0)
    if max_val &gt; 1.0 + 1e-6:
        t_norm = t_f / self.num_train_timesteps
    else:
        t_norm = t_f
    t_norm = t_norm.clamp(min=0.0, max=1.0)

    if weight_type == &quot;uniform&quot;:
        w = torch.ones_like(t_norm)
    elif weight_type == &quot;gaussian&quot;:
        w = torch.exp(-0.5 * ((t_norm - 0.5) / 0.2)**2)
    elif weight_type == &quot;beta08&quot;:
        w = t_norm.pow(1.0) * (1.0 - t_norm).clamp_min(0.0).pow(0.5)
    else:
        raise ValueError(f&quot;Unknown weight_type: &#123;weight_type!r&#125;&quot;)

    denom = w.sum().clamp_min(1e-8)
    return w * (float(self.num_train_timesteps) / denom)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.set_timesteps"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.set_timesteps</code></h3>
<pre class="doc-signature"><code>set_timesteps(*, num_inference_steps: int, device: torch.device | str = &#x27;cpu&#x27;, custom_timesteps: list&#91;float&#93; | torch.Tensor | None = None) -&gt; None</code></pre>
<p>Build a descending timestep schedule ending at 0.</p>
<p>With <code>num_inference_steps=N</code> the schedule has <code>N + 1</code> entries
<code>[T_max, ..., 0]</code> so a rollout consumes <code>N</code> Euler steps.</p>
<p><code>custom_timesteps</code> overrides the linspace+shift schedule with
a pinned list (in absolute train-timestep units), useful for the
AnyFlow paper's hand-tuned <code>[999, 937, 833, 624, 0]</code> schedule.</p>
<details><summary>Source</summary><pre><code>def set_timesteps(
    self,
    *,
    num_inference_steps: int,
    device: torch.device | str = &quot;cpu&quot;,
    custom_timesteps: list&#91;float&#93; | torch.Tensor | None = None,
) -&gt; None:
    &quot;&quot;&quot;Build a descending timestep schedule ending at 0.

    With ``num_inference_steps=N`` the schedule has ``N + 1`` entries
    ``&#91;T_max, ..., 0&#93;`` so a rollout consumes ``N`` Euler steps.

    ``custom_timesteps`` overrides the linspace+shift schedule with
    a pinned list (in absolute train-timestep units), useful for the
    AnyFlow paper&#x27;s hand-tuned ``&#91;999, 937, 833, 624, 0&#93;`` schedule.
    &quot;&quot;&quot;
    if num_inference_steps &lt;= 0:
        raise ValueError(&quot;num_inference_steps must be positive, &quot;
                         f&quot;got &#123;num_inference_steps&#125;&quot;)
    device = torch.device(device)

    if custom_timesteps is not None:
        ts = torch.as_tensor(custom_timesteps, dtype=torch.float32, device=device)
        if ts.ndim != 1:
            raise ValueError(&quot;custom_timesteps must be 1-D, got shape &quot;
                             f&quot;&#123;tuple(ts.shape)&#125;&quot;)
        if not torch.all(ts&#91;:-1&#93; &gt;= ts&#91;1:&#93;):
            raise ValueError(&quot;custom_timesteps must be descending (largest first)&quot;)
    else:
        ts_norm = torch.linspace(1.0, 0.0, num_inference_steps + 1, device=device)
        ts_norm = self.apply_shift(ts_norm)
        ts = ts_norm * self.num_train_timesteps

    self.timesteps = ts
    self.sigmas = ts / self.num_train_timesteps</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.step"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.step</code></h3>
<pre class="doc-signature"><code>step(model_output: torch.Tensor, *, sample: torch.Tensor, timestep: torch.Tensor, r_timestep: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>One Euler step from <code>t</code> to <code>r</code>.</p>
<p><code>model_output</code> is the average-velocity prediction
<code>u_θ(x_t, t, r)</code>. Both <code>timestep</code> and <code>r_timestep</code> are in
absolute train-timestep units (<code>[0, num_train_timesteps]</code>).</p>
<details><summary>Source</summary><pre><code>def step(
    self,
    model_output: torch.Tensor,
    *,
    sample: torch.Tensor,
    timestep: torch.Tensor,
    r_timestep: torch.Tensor,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;One Euler step from ``t`` to ``r``.

    ``model_output`` is the average-velocity prediction
    ``u_θ(x_t, t, r)``. Both ``timestep`` and ``r_timestep`` are in
    absolute train-timestep units (``&#91;0, num_train_timesteps&#93;``).
    &quot;&quot;&quot;
    t = timestep.to(sample.device, dtype=sample.dtype)
    r = r_timestep.to(sample.device, dtype=sample.dtype)
    dt_norm = (t - r) / float(self.num_train_timesteps)
    # Broadcast dt over channel/spatial dims.
    view: list&#91;int&#93; = &#91;-1&#93; + &#91;1&#93; * (sample.ndim - 1)
    return sample - dt_norm.view(*view) * model_output</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.add_noise"><code>fastvideo.models.schedulers.scheduling_flow_map_euler_discrete.FlowMapEulerDiscreteScheduler.add_noise</code></h3>
<pre class="doc-signature"><code>add_noise(original_samples: torch.Tensor, noise: torch.Tensor, timestep: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Linear flow-matching interpolation: <code>x_t = (1 - σ) * x_0 + σ * ε</code>,
where <code>σ = t / num_train_timesteps</code>.</p>
<details><summary>Source</summary><pre><code>def add_noise(
    self,
    original_samples: torch.Tensor,
    noise: torch.Tensor,
    timestep: torch.Tensor,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;Linear flow-matching interpolation: ``x_t = (1 - σ) * x_0 + σ * ε``,
    where ``σ = t / num_train_timesteps``.
    &quot;&quot;&quot;
    sigma = (timestep.to(original_samples.device, dtype=original_samples.dtype) / float(self.num_train_timesteps))
    view: list&#91;int&#93; = &#91;-1&#93; + &#91;1&#93; * (original_samples.ndim - 1)
    sigma = sigma.view(*view)
    return (1.0 - sigma) * original_samples + sigma * noise</code></pre></details>
</div>
