---
title: scheduling_flow_match_euler_discrete
---

<div class="python-reference">
<a id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete-classes"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete-functions"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler-attributes"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler-functions"></a>
<a id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete"></a>

<h2 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.init_logger"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.init_logger</code></h2>
<pre class="doc-signature"><code>init_logger(name: str) -&gt; _FastvideoLogger</code></pre>
<p>The main purpose of this function is to ensure that loggers are
retrieved in such a way that we can be sure the root fastvideo logger has
already been configured.</p>
<h2 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.BaseScheduler"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.BaseScheduler</code></h2>
<pre class="doc-signature"><code>BaseScheduler(*args, **kwargs)</code></pre>


<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.BaseScheduler.set_shift"><code>fastvideo.models.schedulers.scheduling_flow_match_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_match_euler_discrete.BaseScheduler.set_timesteps"><code>fastvideo.models.schedulers.scheduling_flow_match_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_match_euler_discrete.BaseScheduler.scale_model_input"><code>fastvideo.models.schedulers.scheduling_flow_match_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_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput</code></h2>
<pre class="doc-signature"><code>FlowMatchEulerDiscreteSchedulerOutput(prev_sample: torch.FloatTensor)</code></pre>
<p>Output class for the scheduler's <code>step</code> function output.</p>
<h3>Parameters</h3>
<dl>
<dt><code>prev_sample: `torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images</code></dt><dd><p>Computed sample <code>(x_&#123;t-1&#125;)</code> of previous timestep. <code>prev_sample</code> should be used as next model input in the
denoising loop.</p></dd>
</dl>

<details><summary>Source</summary><pre><code>@dataclass
class FlowMatchEulerDiscreteSchedulerOutput(BaseOutput):
    &quot;&quot;&quot;
    Output class for the scheduler&#x27;s `step` function output.

    Args:
        prev_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images):
            Computed sample `(x_&#123;t-1&#125;)` of previous timestep. `prev_sample` should be used as next model input in the
            denoising loop.
    &quot;&quot;&quot;

    prev_sample: torch.FloatTensor</code></pre></details>
<h2 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler</code></h2>
<pre class="doc-signature"><code>FlowMatchEulerDiscreteScheduler(num_train_timesteps: int = 1000, shift: float = 1.0, use_dynamic_shifting: bool = False, base_shift: float | None = 0.5, max_shift: float | None = 1.15, base_image_seq_len: int | None = 256, max_image_seq_len: int | None = 4096, invert_sigmas: bool = False, shift_terminal: float | None = None, use_karras_sigmas: bool | None = False, use_exponential_sigmas: bool | None = False, use_beta_sigmas: bool | None = False, time_shift_type: str = &#x27;exponential&#x27;, stochastic_sampling: bool = False, final_sigmas_type: str = &#x27;sigma_min&#x27;, sigma_max: float | None = None, sigma_min: float | None = None, sigma_data: float | None = None, use_reference_discrete_timesteps: bool = False)</code></pre>
<p>Euler scheduler.</p>
<p>This model inherits from [<code>SchedulerMixin</code>] and [<code>ConfigMixin</code>]. Check the superclass documentation for the generic
methods the library implements for all schedulers such as loading and saving.</p>
<h3>Parameters</h3>
<dl>
<dt><code>num_train_timesteps: `int`, defaults to 1000</code></dt><dd><p>The number of diffusion steps to train the model.</p></dd>
<dt><code>shift: `float`, defaults to 1.0</code></dt><dd><p>The shift value for the timestep schedule.</p></dd>
<dt><code>use_dynamic_shifting: `bool`, defaults to False</code></dt><dd><p>Whether to apply timestep shifting on-the-fly based on the image resolution.</p></dd>
<dt><code>base_shift: `float`, defaults to 0.5</code></dt><dd><p>Value to stabilize image generation. Increasing <code>base_shift</code> reduces variation and image is more consistent
with desired output.</p></dd>
<dt><code>max_shift: `float`, defaults to 1.15</code></dt><dd><p>Value change allowed to latent vectors. Increasing <code>max_shift</code> encourages more variation and image may be
more exaggerated or stylized.</p></dd>
<dt><code>base_image_seq_len: `int`, defaults to 256</code></dt><dd><p>The base image sequence length.</p></dd>
<dt><code>max_image_seq_len: `int`, defaults to 4096</code></dt><dd><p>The maximum image sequence length.</p></dd>
<dt><code>invert_sigmas: `bool`, defaults to False</code></dt><dd><p>Whether to invert the sigmas.</p></dd>
<dt><code>shift_terminal: `float`, defaults to None</code></dt><dd><p>The end value of the shifted timestep schedule.</p></dd>
<dt><code>use_karras_sigmas: `bool`, defaults to False</code></dt><dd><p>Whether to use Karras sigmas for step sizes in the noise schedule during sampling.</p></dd>
<dt><code>use_exponential_sigmas: `bool`, defaults to False</code></dt><dd><p>Whether to use exponential sigmas for step sizes in the noise schedule during sampling.</p></dd>
<dt><code>use_beta_sigmas: `bool`, defaults to False</code></dt><dd><p>Whether to use beta sigmas for step sizes in the noise schedule during sampling.</p></dd>
<dt><code>time_shift_type: `str`, defaults to &quot;exponential&quot;</code></dt><dd><p>The type of dynamic resolution-dependent timestep shifting to apply. Either "exponential" or "linear".</p></dd>
<dt><code>stochastic_sampling: `bool`, defaults to False</code></dt><dd><p>Whether to use stochastic sampling.</p></dd>
<dt><code>final_sigmas_type: `str`, defaults to &quot;sigma_min&quot;</code></dt><dd><p>The type of final sigmas to use. Either "sigma_min" or "zero".</p></dd>
<dt><code>sigma_max: `float`, *optional*</code></dt><dd><p>The maximum sigma value for the noise schedule.</p></dd>
<dt><code>sigma_min: `float`, *optional*</code></dt><dd><p>The minimum sigma value for the noise schedule.</p></dd>
<dt><code>sigma_data: `float`, *optional*</code></dt><dd><p>The sigma data value for scaling.</p></dd>
<dt><code>use_reference_discrete_timesteps: `bool`, defaults to False</code></dt><dd><p>Some reference schedulers (e.g. MMAudio) construct the timestep
schedule by linspacing <code>num_inference_steps + 1</code> points from
<code>t_max</code> to <code>t_min</code> and dropping the terminal point. Default
(<code>False</code>) preserves the original <code>np.linspace(t_max, t_min,
num_inference_steps)</code> (float64) behaviour used by every existing
model. Enable this flag only when matching a reference scheduler
that expects the +1 + drop-terminal construction.</p></dd>
</dl>

<details><summary>Source</summary><pre><code>class FlowMatchEulerDiscreteScheduler(SchedulerMixin, ConfigMixin, BaseScheduler):
    &quot;&quot;&quot;
    Euler scheduler.

    This model inherits from &#91;`SchedulerMixin`&#93; and &#91;`ConfigMixin`&#93;. Check the superclass documentation for the generic
    methods the library implements for all schedulers such as loading and saving.

    Args:
        num_train_timesteps (`int`, defaults to 1000):
            The number of diffusion steps to train the model.
        shift (`float`, defaults to 1.0):
            The shift value for the timestep schedule.
        use_dynamic_shifting (`bool`, defaults to False):
            Whether to apply timestep shifting on-the-fly based on the image resolution.
        base_shift (`float`, defaults to 0.5):
            Value to stabilize image generation. Increasing `base_shift` reduces variation and image is more consistent
            with desired output.
        max_shift (`float`, defaults to 1.15):
            Value change allowed to latent vectors. Increasing `max_shift` encourages more variation and image may be
            more exaggerated or stylized.
        base_image_seq_len (`int`, defaults to 256):
            The base image sequence length.
        max_image_seq_len (`int`, defaults to 4096):
            The maximum image sequence length.
        invert_sigmas (`bool`, defaults to False):
            Whether to invert the sigmas.
        shift_terminal (`float`, defaults to None):
            The end value of the shifted timestep schedule.
        use_karras_sigmas (`bool`, defaults to False):
            Whether to use Karras sigmas for step sizes in the noise schedule during sampling.
        use_exponential_sigmas (`bool`, defaults to False):
            Whether to use exponential sigmas for step sizes in the noise schedule during sampling.
        use_beta_sigmas (`bool`, defaults to False):
            Whether to use beta sigmas for step sizes in the noise schedule during sampling.
        time_shift_type (`str`, defaults to &quot;exponential&quot;):
            The type of dynamic resolution-dependent timestep shifting to apply. Either &quot;exponential&quot; or &quot;linear&quot;.
        stochastic_sampling (`bool`, defaults to False):
            Whether to use stochastic sampling.
        final_sigmas_type (`str`, defaults to &quot;sigma_min&quot;):
            The type of final sigmas to use. Either &quot;sigma_min&quot; or &quot;zero&quot;.
        sigma_max (`float`, *optional*):
            The maximum sigma value for the noise schedule.
        sigma_min (`float`, *optional*):
            The minimum sigma value for the noise schedule.
        sigma_data (`float`, *optional*):
            The sigma data value for scaling.
        use_reference_discrete_timesteps (`bool`, defaults to False):
            Some reference schedulers (e.g. MMAudio) construct the timestep
            schedule by linspacing `num_inference_steps + 1` points from
            `t_max` to `t_min` and dropping the terminal point. Default
            (`False`) preserves the original `np.linspace(t_max, t_min,
            num_inference_steps)` (float64) behaviour used by every existing
            model. Enable this flag only when matching a reference scheduler
            that expects the +1 + drop-terminal construction.
    &quot;&quot;&quot;

    _compatibles: list&#91;Any&#93; = &#91;&#93;
    order = 1

    @register_to_config
    def __init__(
        self,
        num_train_timesteps: int = 1000,
        shift: float = 1.0,
        use_dynamic_shifting: bool = False,
        base_shift: float | None = 0.5,
        max_shift: float | None = 1.15,
        base_image_seq_len: int | None = 256,
        max_image_seq_len: int | None = 4096,
        invert_sigmas: bool = False,
        shift_terminal: float | None = None,
        use_karras_sigmas: bool | None = False,
        use_exponential_sigmas: bool | None = False,
        use_beta_sigmas: bool | None = False,
        time_shift_type: str = &quot;exponential&quot;,
        stochastic_sampling: bool = False,
        final_sigmas_type: str = &quot;sigma_min&quot;,
        sigma_max: float | None = None,
        sigma_min: float | None = None,
        sigma_data: float | None = None,
        use_reference_discrete_timesteps: bool = False,
    ):
        if sum(&#91;self.config.use_beta_sigmas, self.config.use_exponential_sigmas, self.config.use_karras_sigmas&#93;) &gt; 1:
            raise ValueError(
                &quot;Only one of `config.use_beta_sigmas`, `config.use_exponential_sigmas`, `config.use_karras_sigmas` can be used.&quot;
            )
        if time_shift_type not in &#123;&quot;exponential&quot;, &quot;linear&quot;&#125;:
            raise ValueError(&quot;`time_shift_type` must either be &#x27;exponential&#x27; or &#x27;linear&#x27;.&quot;)

        timesteps = np.linspace(1, num_train_timesteps, num_train_timesteps, dtype=np.float32)&#91;::-1&#93;.copy()
        timesteps = torch.from_numpy(timesteps).to(dtype=torch.float32)

        sigmas = timesteps / num_train_timesteps
        if not use_dynamic_shifting:
            # when use_dynamic_shifting is True, we apply the timestep shifting on the fly based on the image resolution
            sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)

        self.timesteps = sigmas * num_train_timesteps
        self.num_train_timesteps = num_train_timesteps

        self._step_index: int | None = None
        self._begin_index: int | None = None

        self._shift = shift

        self.sigmas = sigmas.to(&quot;cpu&quot;)  # to avoid too much CPU/GPU communication
        self.sigma_min = sigma_min if sigma_min is not None else self.sigmas&#91;-1&#93;.item()
        self.sigma_max = self.sigmas&#91;0&#93;.item()

        BaseScheduler.__init__(self)

    @property
    def shift(self) -&gt; float:
        &quot;&quot;&quot;
        The value used for shifting.
        &quot;&quot;&quot;
        return self._shift

    @property
    def step_index(self) -&gt; int | None:
        &quot;&quot;&quot;
        The index counter for current timestep. It will increase 1 after each scheduler step.
        &quot;&quot;&quot;
        return self._step_index

    @property
    def begin_index(self) -&gt; int | None:
        &quot;&quot;&quot;
        The index for the first timestep. It should be set from pipeline with `set_begin_index` method.
        &quot;&quot;&quot;
        return self._begin_index

    # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler.set_begin_index
    def set_begin_index(self, begin_index: int = 0) -&gt; None:
        &quot;&quot;&quot;
        Sets the begin index for the scheduler. This function should be run from pipeline before the inference.

        Args:
            begin_index (`int`):
                The begin index for the scheduler.
        &quot;&quot;&quot;
        self._begin_index = begin_index

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

    def scale_noise(
        self,
        sample: torch.FloatTensor,
        timestep: float | torch.FloatTensor,
        noise: torch.FloatTensor | None = None,
    ) -&gt; torch.FloatTensor:
        &quot;&quot;&quot;
        Forward process in flow-matching

        Args:
            sample (`torch.FloatTensor`):
                The input sample.
            timestep (`int`, *optional*):
                The current timestep in the diffusion chain.

        Returns:
            `torch.FloatTensor`:
                A scaled input sample.
        &quot;&quot;&quot;
        # Make sure sigmas and timesteps have the same device and dtype as original_samples
        sigmas = self.sigmas.to(device=sample.device, dtype=sample.dtype)

        if sample.device.type == &quot;mps&quot; and torch.is_floating_point(timestep):
            # mps does not support float64
            schedule_timesteps = self.timesteps.to(sample.device, dtype=torch.float32)
            assert isinstance(timestep, torch.Tensor)
            timestep = timestep.to(sample.device, dtype=torch.float32)
        else:
            schedule_timesteps = self.timesteps.to(sample.device)
            assert isinstance(timestep, torch.Tensor)
            timestep = timestep.to(sample.device)

        # self.begin_index is None when scheduler is used for training, or pipeline does not implement set_begin_index
        if self.begin_index is None:
            step_indices = &#91;self.index_for_timestep(t, schedule_timesteps) for t in timestep&#93;
        elif self.step_index is not None:
            # add_noise is called after first denoising step (for inpainting)
            step_indices = &#91;self.step_index&#93; * timestep.shape&#91;0&#93;
        else:
            # add noise is called before first denoising step to create initial latent(img2img)
            step_indices = &#91;self.begin_index&#93; * timestep.shape&#91;0&#93;

        sigma = sigmas&#91;step_indices&#93;.flatten()
        while len(sigma.shape) &lt; len(sample.shape):
            sigma = sigma.unsqueeze(-1)

        sample = sigma * noise + (1.0 - sigma) * sample

        return sample

    def _sigma_to_t(self, sigma: float) -&gt; float:
        return sigma * self.config.num_train_timesteps

    def time_shift(self, mu: float, sigma: float, t: torch.Tensor | np.ndarray) -&gt; torch.Tensor | np.ndarray:
        if self.config.time_shift_type == &quot;exponential&quot;:
            return self._time_shift_exponential(mu, sigma, t)
        elif self.config.time_shift_type == &quot;linear&quot;:
            return self._time_shift_linear(mu, sigma, t)
        else:
            raise ValueError(f&quot;Unknown time_shift_type: &#123;self.config.time_shift_type&#125;&quot;)

    def stretch_shift_to_terminal(self, t: torch.Tensor) -&gt; torch.Tensor:
        r&quot;&quot;&quot;
        Stretches and shifts the timestep schedule to ensure it terminates at the configured `shift_terminal` config
        value.

        Reference:
        https://github.com/Lightricks/LTX-Video/blob/a01a171f8fe3d99dce2728d60a73fecf4d4238ae/ltx_video/schedulers/rf.py#L51

        Args:
            t (`torch.Tensor`):
                A tensor of timesteps to be stretched and shifted.

        Returns:
            `torch.Tensor`:
                A tensor of adjusted timesteps such that the final value equals `self.config.shift_terminal`.
        &quot;&quot;&quot;
        one_minus_z = 1 - t
        scale_factor = one_minus_z&#91;-1&#93; / (1 - self.config.shift_terminal)
        stretched_t = 1 - (one_minus_z / scale_factor)
        return stretched_t

    def set_timesteps(
        self,
        num_inference_steps: int | None = None,
        device: str | torch.device = None,
        sigmas: list&#91;float&#93; | None = None,
        mu: float | None = None,
        timesteps: list&#91;float&#93; | None = None,
    ) -&gt; None:
        &quot;&quot;&quot;
        Sets the discrete timesteps used for the diffusion chain (to be run before inference).

        Args:
            num_inference_steps (`int`, *optional*):
                The number of diffusion steps used when generating samples with a pre-trained model.
            device (`str` or `torch.device`, *optional*):
                The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
            sigmas (`List&#91;float&#93;`, *optional*):
                Custom values for sigmas to be used for each diffusion step. If `None`, the sigmas are computed
                automatically.
            mu (`float`, *optional*):
                Determines the amount of shifting applied to sigmas when performing resolution-dependent timestep
                shifting.
            timesteps (`List&#91;float&#93;`, *optional*):
                Custom values for timesteps to be used for each diffusion step. If `None`, the timesteps are computed
                automatically.
        &quot;&quot;&quot;
        if self.config.use_dynamic_shifting and mu is None:
            raise ValueError(&quot;`mu` must be passed when `use_dynamic_shifting` is set to be `True`&quot;)

        if sigmas is not None and timesteps is not None and len(sigmas) != len(timesteps):
            raise ValueError(&quot;`sigmas` and `timesteps` should have the same length&quot;)

        if num_inference_steps is not None:
            if (sigmas is not None
                    and len(sigmas) != num_inference_steps) or (timesteps is not None
                                                                and len(timesteps) != num_inference_steps):
                raise ValueError(
                    &quot;`sigmas` and `timesteps` should have the same length as num_inference_steps, if `num_inference_steps` is provided&quot;
                )
        else:
            if sigmas is not None:
                num_inference_steps = len(sigmas)
            elif timesteps is not None:
                num_inference_steps = len(timesteps)
            else:
                raise ValueError(&quot;Either num_inference_steps, sigmas, or timesteps must be provided&quot;)

        self.num_inference_steps = num_inference_steps

        # 1. Prepare default sigmas
        is_timesteps_provided = timesteps is not None

        timesteps_array: np.ndarray | None = None
        if is_timesteps_provided:
            assert timesteps is not None
            timesteps_array = np.array(timesteps).astype(np.float32)

        sigmas_array: np.ndarray
        if sigmas is None:
            if timesteps_array is None:
                t_max = self._sigma_to_t(self.sigma_max)
                t_min = self._sigma_to_t(self.sigma_min)
                if self.config.use_reference_discrete_timesteps:
                    # Some reference schedulers (for example MMAudio) build a
                    # float64 num_steps+1 linspace and drop the terminal point.
                    timesteps_array = np.linspace(
                        t_max,
                        t_min,
                        num_inference_steps + 1,
                    )&#91;:-1&#93;
                else:
                    # Preserve the original numpy default (float64) here —
                    # casting to float32 silently shifts rounded timestep
                    # values for every existing model that uses this branch.
                    timesteps_array = np.linspace(t_max, t_min, num_inference_steps)
            sigmas_array = timesteps_array / self.config.num_train_timesteps
        else:
            sigmas_array = np.array(sigmas).astype(np.float32)
            num_inference_steps = len(sigmas_array)

        # 2. Perform timestep shifting. Either no shifting is applied, or resolution-dependent shifting of
        #    &quot;exponential&quot; or &quot;linear&quot; type is applied
        if self.config.use_dynamic_shifting:
            assert mu is not None, &quot;mu cannot be None when use_dynamic_shifting is True&quot;
            sigmas_array = self.time_shift(mu, 1.0, sigmas_array)
        else:
            sigmas_array = self.shift * sigmas_array / (1 + (self.shift - 1) * sigmas_array)

        # 3. If required, stretch the sigmas schedule to terminate at the configured `shift_terminal` value
        if self.config.shift_terminal:
            sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
            sigmas_tensor = self.stretch_shift_to_terminal(sigmas_tensor)
            sigmas_array = sigmas_tensor.numpy()

        # 4. If required, convert sigmas to one of karras, exponential, or beta sigma schedules
        if self.config.use_karras_sigmas:
            sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
            sigmas_tensor = self._convert_to_karras(in_sigmas=sigmas_tensor, num_inference_steps=num_inference_steps)
            sigmas_array = sigmas_tensor.numpy()
        elif self.config.use_exponential_sigmas:
            sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
            sigmas_tensor = self._convert_to_exponential(in_sigmas=sigmas_tensor,
                                                         num_inference_steps=num_inference_steps)
            sigmas_array = sigmas_tensor.numpy()
        elif self.config.use_beta_sigmas:
            sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
            sigmas_tensor = self._convert_to_beta(in_sigmas=sigmas_tensor, num_inference_steps=num_inference_steps)
            sigmas_array = sigmas_tensor.numpy()

        # 5. Convert sigmas and timesteps to tensors and move to specified device
        sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32, device=device)
        if not is_timesteps_provided:
            timesteps_tensor = sigmas_tensor * self.config.num_train_timesteps
        else:
            assert timesteps_array is not None
            timesteps_tensor = torch.from_numpy(timesteps_array).to(dtype=torch.float32, device=device)

        # 6. Append the terminal sigma value.
        #    If a model requires inverted sigma schedule for denoising but timesteps without inversion, the
        #    `invert_sigmas` flag can be set to `True`. This case is only required in Mochi
        if self.config.invert_sigmas:
            sigmas_tensor = 1.0 - sigmas_tensor
            timesteps_tensor = sigmas_tensor * self.config.num_train_timesteps
            sigmas_tensor = torch.cat(&#91;sigmas_tensor, torch.ones(1, device=sigmas_tensor.device)&#93;)
        else:
            sigmas_tensor = torch.cat(&#91;sigmas_tensor, torch.zeros(1, device=sigmas_tensor.device)&#93;)

        self.timesteps = timesteps_tensor
        self.sigmas = sigmas_tensor
        self._step_index = None
        self._begin_index = None

    def index_for_timestep(self,
                           timestep: float | torch.FloatTensor,
                           schedule_timesteps: torch.Tensor | None = None) -&gt; int:
        if schedule_timesteps is None:
            schedule_timesteps = self.timesteps

        indices = (schedule_timesteps == timestep).nonzero()

        # The sigma index that is taken for the **very** first `step`
        # is always the second index (or the last index if there is only 1)
        # This way we can ensure we don&#x27;t accidentally skip a sigma in
        # case we start in the middle of the denoising schedule (e.g. for image-to-image)
        pos = 1 if len(indices) &gt; 1 else 0

        return indices&#91;pos&#93;.item()

    def _init_step_index(self, timestep: float | torch.FloatTensor) -&gt; None:
        if self.begin_index is None:
            if isinstance(timestep, torch.Tensor):
                timestep = timestep.to(self.timesteps.device)
            self._step_index = self.index_for_timestep(timestep)
        else:
            self._step_index = self._begin_index

    def step(
        self,
        model_output: torch.FloatTensor,
        timestep: int | torch.Tensor,
        sample: torch.FloatTensor,
        s_churn: float = 0.0,
        s_tmin: float = 0.0,
        s_tmax: float = float(&quot;inf&quot;),
        s_noise: float = 1.0,
        generator: torch.Generator | None = None,
        per_token_timesteps: torch.Tensor | None = None,
        return_dict: bool = True,
    ) -&gt; FlowMatchEulerDiscreteSchedulerOutput | tuple&#91;torch.FloatTensor, ...&#93;:
        &quot;&quot;&quot;
        Predict the sample from the previous timestep by reversing the SDE. This function propagates the diffusion
        process from the learned model outputs (most often the predicted noise).

        Args:
            model_output (`torch.FloatTensor`):
                The direct output from learned diffusion model.
            timestep (`int` or `torch.Tensor`):
                The current discrete timestep in the diffusion chain.
            sample (`torch.FloatTensor`):
                A current instance of a sample created by the diffusion process.
            s_churn (`float`):
            s_tmin  (`float`):
            s_tmax  (`float`):
            s_noise (`float`, defaults to 1.0):
                Scaling factor for noise added to the sample.
            generator (`torch.Generator`, *optional*):
                A random number generator.
            per_token_timesteps (`torch.Tensor`, *optional*):
                The timesteps for each token in the sample.
            return_dict (`bool`):
                Whether or not to return a
                &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; or tuple.

        Returns:
            &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; or `tuple`:
                If return_dict is `True`,
                &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; is returned,
                otherwise a tuple is returned where the first element is the sample tensor.
        &quot;&quot;&quot;

        if (isinstance(timestep, int | torch.IntTensor | torch.LongTensor)):
            raise ValueError((&quot;Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to&quot;
                              &quot; `FlowMatchEulerDiscreteScheduler.step()` is not supported. Make sure to pass&quot;
                              &quot; one of the `scheduler.timesteps` as a timestep.&quot;), )

        if self.step_index is None:
            self._init_step_index(timestep)

        # Upcast to avoid precision issues when computing prev_sample
        sample = sample.to(torch.float32)

        if per_token_timesteps is not None:
            per_token_sigmas = per_token_timesteps / self.config.num_train_timesteps

            sigmas = self.sigmas&#91;:, None, None&#93;
            lower_mask = sigmas &lt; per_token_sigmas&#91;None&#93; - 1e-6
            lower_sigmas = lower_mask * sigmas
            lower_sigmas, _ = lower_sigmas.max(dim=0)

            current_sigma = per_token_sigmas&#91;..., None&#93;
            next_sigma = lower_sigmas&#91;..., None&#93;
            dt = current_sigma - next_sigma
        else:
            if self.step_index is None:
                self._init_step_index(timestep)

            sigma_idx = self.step_index
            sigma = self.sigmas&#91;sigma_idx&#93;
            sigma_next = self.sigmas&#91;sigma_idx + 1&#93;

            current_sigma = sigma
            next_sigma = sigma_next
            dt = sigma_next - sigma

        if self.config.stochastic_sampling:
            x0 = sample - current_sigma * model_output
            noise = torch.randn_like(sample)
            prev_sample = (1.0 - next_sigma) * x0 + next_sigma * noise
        else:
            prev_sample = sample + dt * model_output

        # upon completion increase step index by one
        self._step_index += 1
        if per_token_timesteps is None:
            # Cast sample back to model compatible dtype
            prev_sample = prev_sample.to(model_output.dtype)

        if isinstance(prev_sample, torch.Tensor | float) and not return_dict:
            return (prev_sample, )

        return FlowMatchEulerDiscreteSchedulerOutput(prev_sample=prev_sample)

    # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_karras
    def _convert_to_karras(self, in_sigmas: torch.Tensor, num_inference_steps: int) -&gt; torch.Tensor:
        &quot;&quot;&quot;Constructs the noise schedule of Karras et al. (2022).&quot;&quot;&quot;

        # Hack to make sure that other schedulers which copy this function don&#x27;t break
        # TODO: Add this logic to the other schedulers
        sigma_min = self.config.sigma_min if hasattr(self.config, &quot;sigma_min&quot;) else None

        sigma_max = self.config.sigma_max if hasattr(self.config, &quot;sigma_max&quot;) else None

        sigma_min = sigma_min if sigma_min is not None else in_sigmas&#91;-1&#93;.item()
        sigma_max = sigma_max if sigma_max is not None else in_sigmas&#91;0&#93;.item()

        rho = 7.0  # 7.0 is the value used in the paper
        ramp = np.linspace(0, 1, num_inference_steps)
        min_inv_rho = sigma_min**(1 / rho)
        max_inv_rho = sigma_max**(1 / rho)
        sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho))**rho
        return torch.from_numpy(sigmas).to(dtype=in_sigmas.dtype, device=in_sigmas.device)

    # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_exponential
    def _convert_to_exponential(self, in_sigmas: torch.Tensor, num_inference_steps: int) -&gt; torch.Tensor:
        &quot;&quot;&quot;Constructs an exponential noise schedule.&quot;&quot;&quot;

        # Hack to make sure that other schedulers which copy this function don&#x27;t break
        # TODO: Add this logic to the other schedulers
        sigma_min = self.config.sigma_min if hasattr(self.config, &quot;sigma_min&quot;) else None

        sigma_max = self.config.sigma_max if hasattr(self.config, &quot;sigma_max&quot;) else None

        sigma_min = sigma_min if sigma_min is not None else in_sigmas&#91;-1&#93;.item()
        sigma_max = sigma_max if sigma_max is not None else in_sigmas&#91;0&#93;.item()

        sigmas = np.exp(np.linspace(math.log(sigma_max), math.log(sigma_min), num_inference_steps))
        return torch.from_numpy(sigmas).to(dtype=in_sigmas.dtype, device=in_sigmas.device)

    # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_beta
    def _convert_to_beta(self,
                         in_sigmas: torch.Tensor,
                         num_inference_steps: int,
                         alpha: float = 0.6,
                         beta: float = 0.6) -&gt; torch.Tensor:
        &quot;&quot;&quot;From &quot;Beta Sampling is All You Need&quot; &#91;arXiv:2407.12173&#93; (Lee et. al, 2024)&quot;&quot;&quot;

        # Hack to make sure that other schedulers which copy this function don&#x27;t break
        # TODO: Add this logic to the other schedulers
        sigma_min = self.config.sigma_min if hasattr(self.config, &quot;sigma_min&quot;) else None

        sigma_max = self.config.sigma_max if hasattr(self.config, &quot;sigma_max&quot;) else None

        sigma_min = sigma_min if sigma_min is not None else in_sigmas&#91;-1&#93;.item()
        sigma_max = sigma_max if sigma_max is not None else in_sigmas&#91;0&#93;.item()

        sigmas = np.array(&#91;
            sigma_min + (ppf * (sigma_max - sigma_min)) for ppf in
            &#91;scipy.stats.beta.ppf(timestep, alpha, beta) for timestep in 1 - np.linspace(0, 1, num_inference_steps)&#93;
        &#93;)
        return torch.from_numpy(sigmas).to(dtype=in_sigmas.dtype, device=in_sigmas.device)

    def _time_shift_exponential(self, mu: float, sigma: float,
                                t: torch.Tensor | np.ndarray) -&gt; torch.Tensor | np.ndarray:
        if isinstance(t, np.ndarray):
            return np.exp(mu) / (np.exp(mu) + (1 / t - 1)**sigma)
        else:
            return math.exp(mu) / (math.exp(mu) + (1 / t - 1)**sigma)

    def _time_shift_linear(self, mu: float, sigma: float, t: torch.Tensor | np.ndarray) -&gt; torch.Tensor | np.ndarray:
        return mu / (mu + (1 / t - 1)**sigma)

    def add_noise(
        self,
        clean_latent: torch.Tensor,
        noise: torch.Tensor,
        timestep: torch.IntTensor,
    ) -&gt; torch.Tensor:
        &quot;&quot;&quot;
        Args:
            clean_latent: the clean latent with shape &#91;B, C, H, W&#93;,
                where B is batch_size or batch_size * num_frames
            noise: the noise with shape &#91;B, C, H, W&#93;
            timestep: the timestep with shape &#91;1&#93; or &#91;bs * num_frames&#93; or &#91;bs, num_frames&#93;

        Returns:
            the corrupted latent with shape &#91;B, C, H, W&#93;
        &quot;&quot;&quot;
        # If timestep is &#91;bs, num_frames&#93;
        if timestep.ndim == 2:
            timestep = timestep.flatten(0, 1)
            assert timestep.numel() == clean_latent.shape&#91;0&#93;
        elif timestep.ndim == 1:
            # If timestep is &#91;1&#93;
            if timestep.shape&#91;0&#93; == 1:
                timestep = timestep.expand(clean_latent.shape&#91;0&#93;)
            else:
                assert timestep.numel() == clean_latent.shape&#91;0&#93;
        else:
            raise ValueError(f&quot;&#91;add_noise&#93; Invalid timestep shape: &#123;timestep.shape&#125;&quot;)
        # timestep shape should be &#91;B&#93;
        self.sigmas = self.sigmas.to(noise.device)
        self.timesteps = self.timesteps.to(noise.device)
        timestep_id = torch.argmin((self.timesteps.unsqueeze(0) - timestep.unsqueeze(1)).abs(), dim=1)
        sigma = self.sigmas&#91;timestep_id&#93;.reshape(-1, 1, 1, 1)
        sample = (1 - sigma) * clean_latent + sigma * noise
        return sample.type_as(noise)

    def scale_model_input(self, sample: torch.Tensor, timestep: int | None = None) -&gt; torch.Tensor:
        return sample

    def __len__(self) -&gt; int:
        return self.config.num_train_timesteps</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.shift"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.shift</code></h3>
<pre class="doc-signature"><code>shift: float</code></pre>
<p>The value used for shifting.</p>
<details><summary>Source</summary><pre><code>def shift(self) -&gt; float:
    &quot;&quot;&quot;
    The value used for shifting.
    &quot;&quot;&quot;
    return self._shift</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.step_index"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.step_index</code></h3>
<pre class="doc-signature"><code>step_index: int | None</code></pre>
<p>The index counter for current timestep. It will increase 1 after each scheduler step.</p>
<details><summary>Source</summary><pre><code>def step_index(self) -&gt; int | None:
    &quot;&quot;&quot;
    The index counter for current timestep. It will increase 1 after each scheduler step.
    &quot;&quot;&quot;
    return self._step_index</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.begin_index"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.begin_index</code></h3>
<pre class="doc-signature"><code>begin_index: int | None</code></pre>
<p>The index for the first timestep. It should be set from pipeline with <code>set_begin_index</code> method.</p>
<details><summary>Source</summary><pre><code>def begin_index(self) -&gt; int | None:
    &quot;&quot;&quot;
    The index for the first timestep. It should be set from pipeline with `set_begin_index` method.
    &quot;&quot;&quot;
    return self._begin_index</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_begin_index"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_begin_index</code></h3>
<pre class="doc-signature"><code>set_begin_index(begin_index: int = 0) -&gt; None</code></pre>
<p>Sets the begin index for the scheduler. This function should be run from pipeline before the inference.</p>
<h4>Parameters</h4>
<dl>
<dt><code>begin_index: `int`</code></dt><dd><p>The begin index for the scheduler.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def set_begin_index(self, begin_index: int = 0) -&gt; None:
    &quot;&quot;&quot;
    Sets the begin index for the scheduler. This function should be run from pipeline before the inference.

    Args:
        begin_index (`int`):
            The begin index for the scheduler.
    &quot;&quot;&quot;
    self._begin_index = begin_index</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_shift"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.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 = shift</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.scale_noise"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.scale_noise</code></h3>
<pre class="doc-signature"><code>scale_noise(sample: torch.FloatTensor, timestep: float | torch.FloatTensor, noise: torch.FloatTensor | None = None) -&gt; torch.FloatTensor</code></pre>
<p>Forward process in flow-matching</p>
<h4>Parameters</h4>
<dl>
<dt><code>sample: `torch.FloatTensor`</code></dt><dd><p>The input sample.</p></dd>
<dt><code>timestep: `int`, *optional*</code></dt><dd><p>The current timestep in the diffusion chain.</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.FloatTensor</code></dt><dd><p><code>torch.FloatTensor</code>:
A scaled input sample.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def scale_noise(
    self,
    sample: torch.FloatTensor,
    timestep: float | torch.FloatTensor,
    noise: torch.FloatTensor | None = None,
) -&gt; torch.FloatTensor:
    &quot;&quot;&quot;
    Forward process in flow-matching

    Args:
        sample (`torch.FloatTensor`):
            The input sample.
        timestep (`int`, *optional*):
            The current timestep in the diffusion chain.

    Returns:
        `torch.FloatTensor`:
            A scaled input sample.
    &quot;&quot;&quot;
    # Make sure sigmas and timesteps have the same device and dtype as original_samples
    sigmas = self.sigmas.to(device=sample.device, dtype=sample.dtype)

    if sample.device.type == &quot;mps&quot; and torch.is_floating_point(timestep):
        # mps does not support float64
        schedule_timesteps = self.timesteps.to(sample.device, dtype=torch.float32)
        assert isinstance(timestep, torch.Tensor)
        timestep = timestep.to(sample.device, dtype=torch.float32)
    else:
        schedule_timesteps = self.timesteps.to(sample.device)
        assert isinstance(timestep, torch.Tensor)
        timestep = timestep.to(sample.device)

    # self.begin_index is None when scheduler is used for training, or pipeline does not implement set_begin_index
    if self.begin_index is None:
        step_indices = &#91;self.index_for_timestep(t, schedule_timesteps) for t in timestep&#93;
    elif self.step_index is not None:
        # add_noise is called after first denoising step (for inpainting)
        step_indices = &#91;self.step_index&#93; * timestep.shape&#91;0&#93;
    else:
        # add noise is called before first denoising step to create initial latent(img2img)
        step_indices = &#91;self.begin_index&#93; * timestep.shape&#91;0&#93;

    sigma = sigmas&#91;step_indices&#93;.flatten()
    while len(sigma.shape) &lt; len(sample.shape):
        sigma = sigma.unsqueeze(-1)

    sample = sigma * noise + (1.0 - sigma) * sample

    return sample</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.time_shift"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.time_shift</code></h3>
<pre class="doc-signature"><code>time_shift(mu: float, sigma: float, t: torch.Tensor | np.ndarray) -&gt; torch.Tensor | np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def time_shift(self, mu: float, sigma: float, t: torch.Tensor | np.ndarray) -&gt; torch.Tensor | np.ndarray:
    if self.config.time_shift_type == &quot;exponential&quot;:
        return self._time_shift_exponential(mu, sigma, t)
    elif self.config.time_shift_type == &quot;linear&quot;:
        return self._time_shift_linear(mu, sigma, t)
    else:
        raise ValueError(f&quot;Unknown time_shift_type: &#123;self.config.time_shift_type&#125;&quot;)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.stretch_shift_to_terminal"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.stretch_shift_to_terminal</code></h3>
<pre class="doc-signature"><code>stretch_shift_to_terminal(t: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Stretches and shifts the timestep schedule to ensure it terminates at the configured <code>shift_terminal</code> config
value.</p>
<p>Reference:
https://github.com/Lightricks/LTX-Video/blob/a01a171f8fe3d99dce2728d60a73fecf4d4238ae/ltx_video/schedulers/rf.py#L51</p>
<h4>Parameters</h4>
<dl>
<dt><code>t: `torch.Tensor`</code></dt><dd><p>A tensor of timesteps to be stretched and shifted.</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p><code>torch.Tensor</code>:
A tensor of adjusted timesteps such that the final value equals <code>self.config.shift_terminal</code>.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def stretch_shift_to_terminal(self, t: torch.Tensor) -&gt; torch.Tensor:
    r&quot;&quot;&quot;
    Stretches and shifts the timestep schedule to ensure it terminates at the configured `shift_terminal` config
    value.

    Reference:
    https://github.com/Lightricks/LTX-Video/blob/a01a171f8fe3d99dce2728d60a73fecf4d4238ae/ltx_video/schedulers/rf.py#L51

    Args:
        t (`torch.Tensor`):
            A tensor of timesteps to be stretched and shifted.

    Returns:
        `torch.Tensor`:
            A tensor of adjusted timesteps such that the final value equals `self.config.shift_terminal`.
    &quot;&quot;&quot;
    one_minus_z = 1 - t
    scale_factor = one_minus_z&#91;-1&#93; / (1 - self.config.shift_terminal)
    stretched_t = 1 - (one_minus_z / scale_factor)
    return stretched_t</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_timesteps"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_timesteps</code></h3>
<pre class="doc-signature"><code>set_timesteps(num_inference_steps: int | None = None, device: str | torch.device = None, sigmas: list&#91;float&#93; | None = None, mu: float | None = None, timesteps: list&#91;float&#93; | None = None) -&gt; None</code></pre>
<p>Sets the discrete timesteps used for the diffusion chain (to be run before inference).</p>
<h4>Parameters</h4>
<dl>
<dt><code>num_inference_steps: `int`, *optional*</code></dt><dd><p>The number of diffusion steps used when generating samples with a pre-trained model.</p></dd>
<dt><code>device: `str` or `torch.device`, *optional*</code></dt><dd><p>The device to which the timesteps should be moved to. If <code>None</code>, the timesteps are not moved.</p></dd>
<dt><code>sigmas: `List[float]`, *optional*</code></dt><dd><p>Custom values for sigmas to be used for each diffusion step. If <code>None</code>, the sigmas are computed
automatically.</p></dd>
<dt><code>mu: `float`, *optional*</code></dt><dd><p>Determines the amount of shifting applied to sigmas when performing resolution-dependent timestep
shifting.</p></dd>
<dt><code>timesteps: `List[float]`, *optional*</code></dt><dd><p>Custom values for timesteps to be used for each diffusion step. If <code>None</code>, the timesteps are computed
automatically.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def set_timesteps(
    self,
    num_inference_steps: int | None = None,
    device: str | torch.device = None,
    sigmas: list&#91;float&#93; | None = None,
    mu: float | None = None,
    timesteps: list&#91;float&#93; | None = None,
) -&gt; None:
    &quot;&quot;&quot;
    Sets the discrete timesteps used for the diffusion chain (to be run before inference).

    Args:
        num_inference_steps (`int`, *optional*):
            The number of diffusion steps used when generating samples with a pre-trained model.
        device (`str` or `torch.device`, *optional*):
            The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
        sigmas (`List&#91;float&#93;`, *optional*):
            Custom values for sigmas to be used for each diffusion step. If `None`, the sigmas are computed
            automatically.
        mu (`float`, *optional*):
            Determines the amount of shifting applied to sigmas when performing resolution-dependent timestep
            shifting.
        timesteps (`List&#91;float&#93;`, *optional*):
            Custom values for timesteps to be used for each diffusion step. If `None`, the timesteps are computed
            automatically.
    &quot;&quot;&quot;
    if self.config.use_dynamic_shifting and mu is None:
        raise ValueError(&quot;`mu` must be passed when `use_dynamic_shifting` is set to be `True`&quot;)

    if sigmas is not None and timesteps is not None and len(sigmas) != len(timesteps):
        raise ValueError(&quot;`sigmas` and `timesteps` should have the same length&quot;)

    if num_inference_steps is not None:
        if (sigmas is not None
                and len(sigmas) != num_inference_steps) or (timesteps is not None
                                                            and len(timesteps) != num_inference_steps):
            raise ValueError(
                &quot;`sigmas` and `timesteps` should have the same length as num_inference_steps, if `num_inference_steps` is provided&quot;
            )
    else:
        if sigmas is not None:
            num_inference_steps = len(sigmas)
        elif timesteps is not None:
            num_inference_steps = len(timesteps)
        else:
            raise ValueError(&quot;Either num_inference_steps, sigmas, or timesteps must be provided&quot;)

    self.num_inference_steps = num_inference_steps

    # 1. Prepare default sigmas
    is_timesteps_provided = timesteps is not None

    timesteps_array: np.ndarray | None = None
    if is_timesteps_provided:
        assert timesteps is not None
        timesteps_array = np.array(timesteps).astype(np.float32)

    sigmas_array: np.ndarray
    if sigmas is None:
        if timesteps_array is None:
            t_max = self._sigma_to_t(self.sigma_max)
            t_min = self._sigma_to_t(self.sigma_min)
            if self.config.use_reference_discrete_timesteps:
                # Some reference schedulers (for example MMAudio) build a
                # float64 num_steps+1 linspace and drop the terminal point.
                timesteps_array = np.linspace(
                    t_max,
                    t_min,
                    num_inference_steps + 1,
                )&#91;:-1&#93;
            else:
                # Preserve the original numpy default (float64) here —
                # casting to float32 silently shifts rounded timestep
                # values for every existing model that uses this branch.
                timesteps_array = np.linspace(t_max, t_min, num_inference_steps)
        sigmas_array = timesteps_array / self.config.num_train_timesteps
    else:
        sigmas_array = np.array(sigmas).astype(np.float32)
        num_inference_steps = len(sigmas_array)

    # 2. Perform timestep shifting. Either no shifting is applied, or resolution-dependent shifting of
    #    &quot;exponential&quot; or &quot;linear&quot; type is applied
    if self.config.use_dynamic_shifting:
        assert mu is not None, &quot;mu cannot be None when use_dynamic_shifting is True&quot;
        sigmas_array = self.time_shift(mu, 1.0, sigmas_array)
    else:
        sigmas_array = self.shift * sigmas_array / (1 + (self.shift - 1) * sigmas_array)

    # 3. If required, stretch the sigmas schedule to terminate at the configured `shift_terminal` value
    if self.config.shift_terminal:
        sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
        sigmas_tensor = self.stretch_shift_to_terminal(sigmas_tensor)
        sigmas_array = sigmas_tensor.numpy()

    # 4. If required, convert sigmas to one of karras, exponential, or beta sigma schedules
    if self.config.use_karras_sigmas:
        sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
        sigmas_tensor = self._convert_to_karras(in_sigmas=sigmas_tensor, num_inference_steps=num_inference_steps)
        sigmas_array = sigmas_tensor.numpy()
    elif self.config.use_exponential_sigmas:
        sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
        sigmas_tensor = self._convert_to_exponential(in_sigmas=sigmas_tensor,
                                                     num_inference_steps=num_inference_steps)
        sigmas_array = sigmas_tensor.numpy()
    elif self.config.use_beta_sigmas:
        sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32)
        sigmas_tensor = self._convert_to_beta(in_sigmas=sigmas_tensor, num_inference_steps=num_inference_steps)
        sigmas_array = sigmas_tensor.numpy()

    # 5. Convert sigmas and timesteps to tensors and move to specified device
    sigmas_tensor = torch.from_numpy(sigmas_array).to(dtype=torch.float32, device=device)
    if not is_timesteps_provided:
        timesteps_tensor = sigmas_tensor * self.config.num_train_timesteps
    else:
        assert timesteps_array is not None
        timesteps_tensor = torch.from_numpy(timesteps_array).to(dtype=torch.float32, device=device)

    # 6. Append the terminal sigma value.
    #    If a model requires inverted sigma schedule for denoising but timesteps without inversion, the
    #    `invert_sigmas` flag can be set to `True`. This case is only required in Mochi
    if self.config.invert_sigmas:
        sigmas_tensor = 1.0 - sigmas_tensor
        timesteps_tensor = sigmas_tensor * self.config.num_train_timesteps
        sigmas_tensor = torch.cat(&#91;sigmas_tensor, torch.ones(1, device=sigmas_tensor.device)&#93;)
    else:
        sigmas_tensor = torch.cat(&#91;sigmas_tensor, torch.zeros(1, device=sigmas_tensor.device)&#93;)

    self.timesteps = timesteps_tensor
    self.sigmas = sigmas_tensor
    self._step_index = None
    self._begin_index = None</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.index_for_timestep"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.index_for_timestep</code></h3>
<pre class="doc-signature"><code>index_for_timestep(timestep: float | torch.FloatTensor, schedule_timesteps: torch.Tensor | None = None) -&gt; int</code></pre>

<details><summary>Source</summary><pre><code>def index_for_timestep(self,
                       timestep: float | torch.FloatTensor,
                       schedule_timesteps: torch.Tensor | None = None) -&gt; int:
    if schedule_timesteps is None:
        schedule_timesteps = self.timesteps

    indices = (schedule_timesteps == timestep).nonzero()

    # The sigma index that is taken for the **very** first `step`
    # is always the second index (or the last index if there is only 1)
    # This way we can ensure we don&#x27;t accidentally skip a sigma in
    # case we start in the middle of the denoising schedule (e.g. for image-to-image)
    pos = 1 if len(indices) &gt; 1 else 0

    return indices&#91;pos&#93;.item()</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.step"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.step</code></h3>
<pre class="doc-signature"><code>step(model_output: torch.FloatTensor, timestep: int | torch.Tensor, sample: torch.FloatTensor, s_churn: float = 0.0, s_tmin: float = 0.0, s_tmax: float = float(&#x27;inf&#x27;), s_noise: float = 1.0, generator: torch.Generator | None = None, per_token_timesteps: torch.Tensor | None = None, return_dict: bool = True) -&gt; FlowMatchEulerDiscreteSchedulerOutput | tuple&#91;torch.FloatTensor, ...&#93;</code></pre>
<p>Predict the sample from the previous timestep by reversing the SDE. This function propagates the diffusion
process from the learned model outputs (most often the predicted noise).</p>
<h4>Parameters</h4>
<dl>
<dt><code>model_output: `torch.FloatTensor`</code></dt><dd><p>The direct output from learned diffusion model.</p></dd>
<dt><code>timestep: `int` or `torch.Tensor`</code></dt><dd><p>The current discrete timestep in the diffusion chain.</p></dd>
<dt><code>sample: `torch.FloatTensor`</code></dt><dd><p>A current instance of a sample created by the diffusion process.</p></dd>
<dt><code>s_churn: `float`</code></dt><dd></dd>
<dt><code>s_tmin: `float`</code></dt><dd></dd>
<dt><code>s_tmax: `float`</code></dt><dd></dd>
<dt><code>s_noise: `float`, defaults to 1.0</code></dt><dd><p>Scaling factor for noise added to the sample.</p></dd>
<dt><code>generator: `torch.Generator`, *optional*</code></dt><dd><p>A random number generator.</p></dd>
<dt><code>per_token_timesteps: `torch.Tensor`, *optional*</code></dt><dd><p>The timesteps for each token in the sample.</p></dd>
<dt><code>return_dict: `bool`</code></dt><dd><p>Whether or not to return a
[<code>~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput</code>] or tuple.</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>FlowMatchEulerDiscreteSchedulerOutput | tuple[torch.FloatTensor, ...]</code></dt><dd><p>[<code>~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput</code>] or <code>tuple</code>:
If return_dict is <code>True</code>,
[<code>~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput</code>] is returned,
otherwise a tuple is returned where the first element is the sample tensor.</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def step(
    self,
    model_output: torch.FloatTensor,
    timestep: int | torch.Tensor,
    sample: torch.FloatTensor,
    s_churn: float = 0.0,
    s_tmin: float = 0.0,
    s_tmax: float = float(&quot;inf&quot;),
    s_noise: float = 1.0,
    generator: torch.Generator | None = None,
    per_token_timesteps: torch.Tensor | None = None,
    return_dict: bool = True,
) -&gt; FlowMatchEulerDiscreteSchedulerOutput | tuple&#91;torch.FloatTensor, ...&#93;:
    &quot;&quot;&quot;
    Predict the sample from the previous timestep by reversing the SDE. This function propagates the diffusion
    process from the learned model outputs (most often the predicted noise).

    Args:
        model_output (`torch.FloatTensor`):
            The direct output from learned diffusion model.
        timestep (`int` or `torch.Tensor`):
            The current discrete timestep in the diffusion chain.
        sample (`torch.FloatTensor`):
            A current instance of a sample created by the diffusion process.
        s_churn (`float`):
        s_tmin  (`float`):
        s_tmax  (`float`):
        s_noise (`float`, defaults to 1.0):
            Scaling factor for noise added to the sample.
        generator (`torch.Generator`, *optional*):
            A random number generator.
        per_token_timesteps (`torch.Tensor`, *optional*):
            The timesteps for each token in the sample.
        return_dict (`bool`):
            Whether or not to return a
            &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; or tuple.

    Returns:
        &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; or `tuple`:
            If return_dict is `True`,
            &#91;`~schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteSchedulerOutput`&#93; is returned,
            otherwise a tuple is returned where the first element is the sample tensor.
    &quot;&quot;&quot;

    if (isinstance(timestep, int | torch.IntTensor | torch.LongTensor)):
        raise ValueError((&quot;Passing integer indices (e.g. from `enumerate(timesteps)`) as timesteps to&quot;
                          &quot; `FlowMatchEulerDiscreteScheduler.step()` is not supported. Make sure to pass&quot;
                          &quot; one of the `scheduler.timesteps` as a timestep.&quot;), )

    if self.step_index is None:
        self._init_step_index(timestep)

    # Upcast to avoid precision issues when computing prev_sample
    sample = sample.to(torch.float32)

    if per_token_timesteps is not None:
        per_token_sigmas = per_token_timesteps / self.config.num_train_timesteps

        sigmas = self.sigmas&#91;:, None, None&#93;
        lower_mask = sigmas &lt; per_token_sigmas&#91;None&#93; - 1e-6
        lower_sigmas = lower_mask * sigmas
        lower_sigmas, _ = lower_sigmas.max(dim=0)

        current_sigma = per_token_sigmas&#91;..., None&#93;
        next_sigma = lower_sigmas&#91;..., None&#93;
        dt = current_sigma - next_sigma
    else:
        if self.step_index is None:
            self._init_step_index(timestep)

        sigma_idx = self.step_index
        sigma = self.sigmas&#91;sigma_idx&#93;
        sigma_next = self.sigmas&#91;sigma_idx + 1&#93;

        current_sigma = sigma
        next_sigma = sigma_next
        dt = sigma_next - sigma

    if self.config.stochastic_sampling:
        x0 = sample - current_sigma * model_output
        noise = torch.randn_like(sample)
        prev_sample = (1.0 - next_sigma) * x0 + next_sigma * noise
    else:
        prev_sample = sample + dt * model_output

    # upon completion increase step index by one
    self._step_index += 1
    if per_token_timesteps is None:
        # Cast sample back to model compatible dtype
        prev_sample = prev_sample.to(model_output.dtype)

    if isinstance(prev_sample, torch.Tensor | float) and not return_dict:
        return (prev_sample, )

    return FlowMatchEulerDiscreteSchedulerOutput(prev_sample=prev_sample)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.add_noise"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.add_noise</code></h3>
<pre class="doc-signature"><code>add_noise(clean_latent: torch.Tensor, noise: torch.Tensor, timestep: torch.IntTensor) -&gt; torch.Tensor</code></pre>
<h4>Parameters</h4>
<dl>
<dt><code>clean_latent: torch.Tensor</code></dt><dd><p>the clean latent with shape [B, C, H, W],
where B is batch_size or batch_size * num_frames</p></dd>
<dt><code>noise: torch.Tensor</code></dt><dd><p>the noise with shape [B, C, H, W]</p></dd>
<dt><code>timestep: torch.IntTensor</code></dt><dd><p>the timestep with shape [1] or [bs * num_frames] or [bs, num_frames]</p></dd>
</dl>
<h4>Returns</h4>
<dl>
<dt><code>torch.Tensor</code></dt><dd><p>the corrupted latent with shape [B, C, H, W]</p></dd>
</dl>
<details><summary>Source</summary><pre><code>def add_noise(
    self,
    clean_latent: torch.Tensor,
    noise: torch.Tensor,
    timestep: torch.IntTensor,
) -&gt; torch.Tensor:
    &quot;&quot;&quot;
    Args:
        clean_latent: the clean latent with shape &#91;B, C, H, W&#93;,
            where B is batch_size or batch_size * num_frames
        noise: the noise with shape &#91;B, C, H, W&#93;
        timestep: the timestep with shape &#91;1&#93; or &#91;bs * num_frames&#93; or &#91;bs, num_frames&#93;

    Returns:
        the corrupted latent with shape &#91;B, C, H, W&#93;
    &quot;&quot;&quot;
    # If timestep is &#91;bs, num_frames&#93;
    if timestep.ndim == 2:
        timestep = timestep.flatten(0, 1)
        assert timestep.numel() == clean_latent.shape&#91;0&#93;
    elif timestep.ndim == 1:
        # If timestep is &#91;1&#93;
        if timestep.shape&#91;0&#93; == 1:
            timestep = timestep.expand(clean_latent.shape&#91;0&#93;)
        else:
            assert timestep.numel() == clean_latent.shape&#91;0&#93;
    else:
        raise ValueError(f&quot;&#91;add_noise&#93; Invalid timestep shape: &#123;timestep.shape&#125;&quot;)
    # timestep shape should be &#91;B&#93;
    self.sigmas = self.sigmas.to(noise.device)
    self.timesteps = self.timesteps.to(noise.device)
    timestep_id = torch.argmin((self.timesteps.unsqueeze(0) - timestep.unsqueeze(1)).abs(), dim=1)
    sigma = self.sigmas&#91;timestep_id&#93;.reshape(-1, 1, 1, 1)
    sample = (1 - sigma) * clean_latent + sigma * noise
    return sample.type_as(noise)</code></pre></details>
<h3 id="fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.scale_model_input"><code>fastvideo.models.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.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:
    return sample</code></pre></details>
</div>
