huggingface / huggingface/diffusers
`FlowMatchEulerDiscreteScheduler.set_timesteps` silently ignores explicit `timesteps` since #14011 — intended semantics change or unreleased regression?
- Dominant language
- Python
- Stars
- 34.5k
- Forks
- 7.3k
- Avg merge
- 3d 3h
- Merged PRs (30d)
- 91
Description
### Describe the bug
Since #14011 (merged 2026-07-07, not yet in any release — v0.39.0 predates it), `FlowMatchEulerDiscreteScheduler.set_timesteps` unconditionally recomputes timesteps from the shifted sigmas ([`scheduling_flow_match_euler_discrete.py#L367`](https://github.com/huggingface/diffusers/blob/614ae4bb9df07d102ce1187777ef1c62f8aab0e8/src/diffusers/schedulers/scheduling_flow_match_euler_discrete.py#L367)), so an explicitly passed custom `timesteps` list is silently discarded. The docstring still promises "Custom values for timesteps **to be used** for each diffusion step" ([L305-L307](https://github.com/huggingface/diffusers/blob/614ae4bb9df07d102ce1187777ef1c62f8aab0e8/src/diffusers/schedulers/scheduling_flow_match_euler_discrete.py#L305-L307)), and `FlowMatchLCMScheduler` still honors the argument ([`scheduling_flow_match_lcm.py#L376-L379`](https://github.com/huggingface/diffusers/blob/614ae4bb9df07d102ce1187777ef1c62f8aab0e8/src/diffusers/schedulers/scheduling_flow_match_lcm.py#L376-L379)), so the two flow-match schedulers now disagree on the same argument.
Concrete impact inside the library: CogView4 / CogView4-Control / GLM-Image deliberately pass integer-cast timesteps plus paired sigmas ([`pipeline_cogview4.py#L587-L606`](https://github.com/huggingface/diffusers/blob/614ae4bb9df07d102ce1187777ef1c62f8aab0e8/src/diffusers/pipelines/cogview4/pipeline_cogview4.py#L587-L606) — a mechanism the model authors added in #10649) and feed `scheduler.timesteps` to the transformer as conditioning. With the official CogView4-6B scheduler config at 1024x1024, the conditioning timesteps now deviate from the passed values by up to **285.75 / 1000** (repro below). The #14011 discussion covered the SD3/Flux pattern (deriving timesteps when only sigmas are given) and did not mention these pipelines.
**Question for the maintainers: is discarding explicit `timesteps` the intended new semantics after #14011?**
- If **intended**: the docstring, the now-dead `is_timesteps_provided` variable ([L329](https://github.com/huggingface/diffusers/blob/614ae4bb9df07d102ce1187777ef1c62f8aab0e8/src/diffusers/schedulers/scheduling_flow_match_euler_discrete.py#L329)), `FlowMatchLCMScheduler`, and the now-ineffective integer-cast in the CogView4/GLM-Image pipelines should be aligned — I'm happy to submit that PR.
- If **not intended**: restoring the explicit-`timesteps` branch (while keeping #14011's recomputation for the derived case) before v0.40 ships would avoid releasing a silent behavior change — I'm happy to submit that PR instead; a minimal fix plus regression test is already prepared on my fork.
### Reproduction
Repro 1 — exactly what `pipeline_cogview4.py` passes (CogView4-6B config, 1024x1024, 10 steps):
```python
import numpy as np
from diffusers import FlowMatchEulerDiscreteScheduler
# THUDM/CogView4-6B scheduler config; mu=3.25 is what calculate_shift() yields at 1024x1024
sched = FlowMatchEulerDiscreteScheduler(
num_train_timesteps=1000, shift=1.0, use_dynamic_shifting=True,
base_shift=0.25, max_shift=0.75, base_image_seq_len=256,
max_image_seq_len=4096, time_shift_type="linear")
timesteps = np.linspace(1000, 1.0, 10).astype(np.int64).astype(np.float32) # as pipeline_cogview4.py does
sched.set_timesteps(10, sigmas=(timesteps / 1000).tolist(), timesteps=timesteps.tolist(), mu=3.25)
print(sched.timesteps.tolist()) # expected: the passed values [1000.0, 889.0, ..., 1.0]
```
Output on `main` (614ae4b):
```
passed : [1000.0, 889.0, 778.0, 667.0, 556.0, 445.0, 334.0, 223.0, 112.0, 1.0]
got : [1000.0, 963.0, 919.29, 866.84, 802.75, 722.67, 619.75, 482.6, 290.73, 3.24]
max delta: 285.75
```
Before #14011 (all releases through v0.39.0), the output equals the passed values.
Repro 2 — divergence from FlowMatchLCMScheduler (same inputs, static shift=3.0)
```python
from diffusers import FlowMatchEulerDiscreteScheduler
from diffusers.schedulers.scheduling_flow_match_lcm import FlowMatchLCMScheduler
ts = [1000.0, 750.0, 500.0, 250.0, 1.0]
sg = [t / 1000 for t in ts]
euler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=1000, shift=3.0)
lcm = FlowMatchLCMScheduler(num_train_timesteps=1000, shift=3.0)
euler.set_timesteps(sigmas=sg, timesteps=ts)
lcm.set_timesteps(sigmas=sg, timesteps=ts)
print(euler.timesteps.tolist()) # [1000.0, 900.0, 750.0, 500.0, 2.99] <- input ignored
print(lcm.timesteps.tolist()) # [1000.0, 750.0, 500.0, 250.0, 1.0] <- input honored
```
(Both produce identical `sigmas`; only `timesteps` diverge.)
### Logs
```shell
(see outputs inline above)
```
### System Info
- 🤗 Diffusers version: 0.40.0.dev0 (main @ 614ae4b)
- Platform: macOS-26.5.2-arm64-arm-64bit-Mach-O
- Running on Google Colab?: No
- Python version: 3.13.3
- PyTorch version (GPU?): 2.13.0 (False)
- Huggingface_hub version: 1.27.0
- Transformers version: 5.15.0
- Accelerate version: not installed
- PEFT version: not installed
- Safetensors version: 0.8.0
- xFormers version: not installed
- Accelerator: Apple M4
- Using GPU in script?: No
- Using distributed or parallel set-up in script?: No
### Who can help?
_No response_
---
*Disclosure: this report was prepared with AI assistance following the "AI-assisted and agentic contributions" guide; I reproduced the issue locally at 614ae4b and reviewed every claim. Per the guide, I'm coordinating here first and will wait for maintainer acknowledgment before opening any PR.*
Contributor guide
Research direction
Start with src/diffusers/schedulers/scheduling_flow_match_euler_discrete.py around set_timesteps and compare its explicit-timesteps handling with scheduling_flow_match_lcm.py. Run the two inline reproductions and inspect the cited CogView4 and GLM-Image pipeline call sites. Done means the intended semantics are confirmed, documented or restored consistently, and covered by a regression test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, pytorch
- Domain
- machine-learning, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100