DDIM Timestep Subsequence
Problem Statement
Build the strided timestep subsequence a DDIM sampler walks when it generates an image in 50 steps from a model trained with 1000, together with the lookup that handles the final step.
Background
Because the DDIM update only ever references at the current and target timesteps, nothing forces the sampler to visit every trained timestep. It can walk any decreasing subsequence , which is why DDIM turns 1000 network evaluations into 20-50 with little quality loss.
The standard uniform stride, as implemented in diffusers, is:
- step = T // num_steps (integer division).
- Take 0, step, 2*step, ..., keeping the first num_steps values.
- Reverse to get descending sampling order.
- The target of timestep t is t - step. For the last step this goes negative, which signals "target is clean data": .
That final convention is final_alpha_cumprod = 1.0 in the reference code, and it is what lets the last step land on exactly.
Your Task
Implement two functions:
def ddim_timesteps(T, num_steps):
def alpha_bar_at(alpha_bars, t):
ddim_timesteps returns a list of (t, t_prev) tuples of Python ints in descending sampling order, with t_prev set to -1 whenever the strided target falls below zero. alpha_bar_at returns float(alpha_bars[t]), or 1.0 when t < 0.
Input Format
- T (int): number of training timesteps.
- num_steps (int): number of sampling steps, 1 <= num_steps <= T.
- alpha_bars: 1-D NumPy array.
- t (int): index, possibly negative.
Output Format
ddim_timesteps returns a list of 2-tuples of ints; alpha_bar_at returns a float.
Sample
print(ddim_timesteps(1000, 5))
The stride is 200, so the ascending grid is [0, 200, 400, 600, 800]; reversed and paired with t - 200 this gives [(800, 600), (600, 400), (400, 200), (200, 0), (0, -1)].
Example
print(ddim_timesteps(1000, 5))
[(800, 600), (600, 400), (400, 200), (200, 0), (0, -1)]
step = 1000 // 5 = 200, so the ascending grid is [0, 200, 400, 600, 800]. Reversed, sampling walks 800, 600, 400, 200, 0, and each target is 200 lower. The last target would be -200, which is below zero, so it is reported as -1 -- the signal that alpha_bar_prev is 1 and this step lands on clean data.
Constraints
- Use integer division for the stride;
Tneed not be divisible bynum_steps. - Keep exactly
num_stepstimesteps --np.arange(0, T, step)can return one extra when the division is inexact. - Return Python
ints inside the tuples, not NumPy scalars (the tuples are printed directly). t_previs-1, never a smaller negative number.alpha_bar_atmust return1.0for any negativet, and never index with a negative number.