Fix FlowMatchEulerDiscreteScheduler.index_for_timestep float precision lookup#13785
Open
ATOM00blue wants to merge 1 commit into
Open
Conversation
`self.timesteps` is computed as `sigmas * num_train_timesteps` in float32, so conceptually integer timesteps can carry a small rounding error (e.g. `254` becomes `254.00001`). The exact `==` lookup in `index_for_timestep` then returns no match and `indices[pos]` raises `IndexError`. This affects the training path of `scale_noise`/`add_noise` (where `begin_index is None` and integer timesteps are commonly passed) as well as direct calls to `index_for_timestep`. Fall back to a `torch.isclose` match when the exact lookup is empty. The tolerance is far smaller than the spacing between timesteps, so genuinely unknown timesteps still raise and exact elements still resolve to their own index. Fixes huggingface#11749
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a float-precision edge case in FlowMatchEulerDiscreteScheduler.index_for_timestep where timesteps that are conceptually integer values (but stored as float32 with small rounding error) could fail an exact == lookup and trigger an IndexError. This affects training-path calls like scale_noise/add_noise when integer timesteps are passed.
Changes:
- Add a fallback
torch.isclose-based lookup inindex_for_timestepwhen exact equality finds no matches. - Add a dedicated scheduler test file covering integer-timestep lookup, exact-value lookup, unknown-timestep rejection, and the
scale_noisetraining path.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/diffusers/schedulers/scheduling_flow_match_euler_discrete.py |
Adds an isclose fallback when exact timestep lookup fails to avoid IndexError from float32 rounding. |
tests/schedulers/test_scheduler_flow_match_euler.py |
Adds regression tests for the float-precision timestep indexing bug and related training-path behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| rounded = timesteps.round() | ||
| mismatched = (timesteps != rounded).nonzero() | ||
| self.assertGreater(mismatched.numel(), 0, "expected at least one timestep with float rounding error") | ||
| index = int(mismatched[0]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
FlowMatchEulerDiscreteScheduler.index_for_timestepraisesIndexErrorfor timesteps that are conceptually integers (e.g.254) because of float32 rounding inself.timesteps.Fixes #11749
Bug
Root cause
self.timestepsis computed assigmas * num_train_timestepsand stored infloat32, so values that should be integers carry a tiny rounding error (254->254.00001).index_for_timesteplooks them up with an exact equality:When the caller passes the integer value (very common in the training path of
scale_noise/add_noise, wherebegin_index is None), the exact match is empty andindices[pos]raisesIndexError. The normal inference loop is unaffected because it passes the exact float elements ofscheduler.timesteps.Fix
Fall back to a
torch.isclosematch only when the exact lookup returns nothing. The float tolerance is far smaller than the spacing between consecutive timesteps (~1), so:Testing
Added
tests/schedulers/test_scheduler_flow_match_euler.pycovering: integer-timestep lookup, thescale_noisetraining path, exact-value lookups, and rejection of an unknown timestep. The two integer-timestep tests fail onmainand pass with this change.ruff check/ruff formatare clean andutils/check_copies.pyreports no issues.Before submitting
Who can review?
@yiyixuxu @hlky
Developed with AI coding assistance; reviewed and submitted by the author.