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 τS>τS−1>⋯>τ1, 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": αˉprev=1.
That final convention is final_alpha_cumprod = 1.0 in the reference code, and it is what lets the last step land on x^0 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.
1. Background Knowledge
Diffusion Models operate by gradually adding noise to data over a fixed number of timesteps T (typically 1000) during training, and then learning to reverse this process. The forward process is defined by a variance schedule βt, and the cumulative noise coefficient is denoted as αˉt=∏s=1t(1−βs). During sampling, the model predicts the noise or the original image x0 from the noisy input xt.
DDIM (Denoising Diffusion Implicit Models) is a deterministic sampling method that allows for faster generation by skipping timesteps. Unlike DDPM, which requires visiting every timestep from T down to 1, DDIM only needs the values of αˉt and αˉt−1 (or rather, αˉtprev) to compute the next state. This property enables strided sampling, where the sampler jumps from t to t−step, significantly reducing the number of network evaluations from T to S (e.g., 50 steps).
The key insight for this problem is the timestep indexing convention. In many implementations (like diffusers), the timesteps are 0-indexed. The sampling process starts at the highest timestep in the subsequence and moves downwards. The "target" timestep for a current timestep t is tprev=t−step. If tprev<0, it indicates that the next state is the clean data x0, which corresponds to αˉ=1.0. This special case must be handled explicitly in the lookup function.
2. Algorithm Approach
The problem requires implementing two distinct but related functions: one for generating the sampling schedule and one for safely retrieving noise coefficients.
- Stride Calculation: The core of the schedule generation is determining the step size. Given T training timesteps and S desired sampling steps, the stride is calculated using integer division: step=T//S. This ensures the steps are evenly distributed across the range [0,T−1].
- Grid Generation: Create an ascending list of timesteps starting from 0 with the calculated stride. We need exactly S timesteps. The sequence is 0,step,2⋅step,…,(S−1)⋅step.
- Reversal and Pairing: Since diffusion sampling goes from noisy (high t) to clean (low t), reverse the generated list. Then, pair each timestep t with its target tprev=t−step.
- Boundary Handling: If tprev<0, set it to −1. This sentinel value signals the lookup function to return 1.0.
- Safe Lookup: The alpha_bar_at function must handle the sentinel value −1 by returning 1.0 instead of accessing an invalid array index.
3. Step-by-Step Strategy
Implementing ddim_timesteps(T, num_steps)
Continue the full explanation
You're reading the free preview. Unlock the complete walkthrough, the code editor, test runner and reference solution with Premium.
Editor locked
The code editor is locked for Pro problems. It is only available for free problems. Please upgrade to gain access to the code editor for all problems.