TensorTonic
Study Plans
Study Plan
Toy DDPM Sampler
Diffusion ModelsSamplingGenerative Models
Hard

Generate samples by running the full DDPM reverse process with a tiny noise-prediction network, for t = T - 1 down to 0.

x^0=xt−1−αˉt ϵ^αˉt,μ=βtαˉt−11−αˉt x^0+(1−αˉt−1)αt1−αˉt xtxt−1=μ+β~t  noise[T−1−t],β~t=βt(1−αˉt−1)1−αˉt\begin{aligned} \hat x_0 &= \frac{x_t - \sqrt{1 - \bar\alpha_t}\, \hat\epsilon}{\sqrt{\bar\alpha_t}}, \qquad \mu = \frac{\beta_t \sqrt{\bar\alpha_{t-1}}}{1 - \bar\alpha_t}\, \hat x_0 + \frac{(1 - \bar\alpha_{t-1}) \sqrt{\alpha_t}}{1 - \bar\alpha_t}\, x_t \\ x_{t-1} &= \mu + \sqrt{\tilde\beta_t}\; \text{noise}[T - 1 - t], \qquad \tilde\beta_t = \frac{\beta_t (1 - \bar\alpha_{t-1})}{1 - \bar\alpha_t} \end{aligned}x^0​xt−1​​=αˉt​​xt​−1−αˉt​​ϵ^​,μ=1−αˉt​βt​αˉt−1​​​x^0​+1−αˉt​(1−αˉt−1​)αt​​​xt​=μ+β~​t​​noise[T−1−t],β~​t​=1−αˉt​βt​(1−αˉt−1​)​​

Here βt\beta_tβt​ is betas[t], αt=1−βt\alpha_t = 1 - \beta_tαt​=1−βt​, αˉt=α0⋯αt\bar\alpha_t = \alpha_0 \cdots \alpha_tαˉt​=α0​⋯αt​ and αˉ−1=1\bar\alpha_{-1} = 1αˉ−1​=1. The loop starts from x_T, which is the sample at timestep T - 1, and step k = T - 1 - t uses noise[k]. At t = 0 the step returns μ\muμ with no noise. When clip_value is a number, x^0\hat x_0x^0​ is clamped to [-clip_value, clip_value] before μ\muμ is computed, and when it is None nothing is clamped.

The prediction is ϵ^=SiLU⁡([ xt,e(t) ]W1+b1)W2+b2\hat\epsilon = \operatorname{SiLU}([\,x_t, e(t)\,] W_1 + b_1) W_2 + b_2ϵ^=SiLU([xt​,e(t)]W1​+b1​)W2​+b2​ with params W1 (D + embed_dim, hidden), b1 (hidden), W2 (hidden, D), b2 (D) and SiLU⁡(z)=z⋅sigmoid⁡(z)\operatorname{SiLU}(z) = z \cdot \operatorname{sigmoid}(z)SiLU(z)=z⋅sigmoid(z). The embedding e(t)=[cos⁡(tf),sin⁡(tf)]e(t) = [\cos(t f), \sin(t f)]e(t)=[cos(tf),sin(tf)] uses half = embed_dim // 2 and fi=exp⁡(−ln⁡(10000) i/half)f_i = \exp(-\ln(10000)\, i / \text{half})fi​=exp(−ln(10000)i/half) for i<halfi < \text{half}i<half, plus one zero when embed_dim is odd, and is the same for all N rows.

Return a dict with "samples" (the final xxx, shape (N, D)) and "pred_x0" (shape (T, N, D), row k is the possibly clamped x^0\hat x_0x^0​ of step k), both with the dtype of x_T.

Examples

Input: x_T = [[1.0,-0.5],[0.2,0.8]], betas = [0.1,0.2,0.3], noise = [[[0.5,-1.0],[0.0,0.3]],[[-0.2,0.4],[1.0,-0.6]],[[0.7,0.7],[-0.7,0.1]]], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, clip_value = null

Output: {"samples":[[1.765787,-1.152671],[0.468101,0.89931]],"pred_x0":[[[1.594648,-0.79624],[0.289775,0.948434]],[[1.881834,-1.28839],[0.248787,1.053894]],[[1.765787,-1.152671],[0.468101,0.89931]]]}

Explanation: The schedule gives alpha_bars of 0.9, 0.72 and 0.504, and the loop visits t = 2, 1, 0 using noise[0] and noise[1]. The last step at t = 0 returns its posterior mean, which equals that step's predicted clean sample, so noise[2] is never used.

Input: x_T = [[2.0,-1.5],[-0.4,1.2],[0.0,0.0]], betas = [0.05,0.25,0.5,0.5], noise = [[[0.1,0.2],[-0.3,0.4],[0.5,-0.6]],[[1.0,0.0],[0.0,1.0],[-1.0,0.5]],[[0.3,-0.3],[0.2,0.2],[-0.4,0.1]],[[0.9,0.9],[0.9,0.9],[0.9,0.9]]], params = {"W1":[[0.6,-0.4],[0.2,0.5],[0.3,-0.1],[-0.2,0.4],[0.1,0.1]],"b1":[0.1,-0.1],"W2":[[0.7,-0.2],[0.3,0.6]],"b2":[0.0,0.0]}, embed_dim = 3, clip_value = 1.0

Output: {"samples":[[1.0,-1.0],[-1.0,1.0],[-0.040924,-0.336405]],"pred_x0":[[[1.0,-1.0],[-1.0,1.0],[0.131976,-0.079674]],[[1.0,-1.0],[-1.0,1.0],[0.64493,-0.670506]],[[1.0,-0.998199],[-1.0,1.0],[0.061717,-0.38619]],[[1.0,-1.0],[-1.0,1.0],[-0.040924,-0.336405]]]}

Input: x_T = [[0.5,-0.5]], betas = [0.2], noise = [[[3.0,-3.0]]], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, clip_value = null

Output: {"samples":[[0.535208,-0.563034]],"pred_x0":[[[0.535208,-0.563034]]]}

Hint 1

Build alpha_bars once with torch.cumprod and treat the value before index 0 as 1.

Hint 2

Iterating with enumerate over range(T - 1, -1, -1) gives both the step index for noise and the timestep t.

Hint 3

torch.cat of the cos, sin and zero blocks builds one embedding row, and .expand(N, embed_dim) repeats it for every sample.

Hint 4

tensor.clamp(-c, c) clips values.

Requirements

  • Use PyTorch tensor operations; torch.nn.functional is allowed.
  • Use the posterior variance tilde beta for the noise scale, never betas[t].
  • Leave every input unchanged, including the tensors in params.

Constraints

  • x_T is a float tensor of shape (N, D) with N between 1 and 8 and D between 1 and 3.
  • betas is a float tensor of shape (T,) with T between 1 and 20 and values in [0.01, 0.5].
  • noise is a float tensor of shape (T, N, D).
  • params has exactly the keys W1 (D + embed_dim, hidden), b1 (hidden), W2 (hidden, D) and b2 (D), with hidden between 1 and 8.
  • embed_dim is an integer between 2 and 8, and clip_value is None or a positive number.
  • All float tensors share one dtype, float32 or float64, and all values are finite.
Try Similar Problems
DDIM StepHardDDPM Ancestral StepMediumKarras Euler SamplerHardDiT Patchify and adaLN-Zero BlockHardToy Diffusion Training StepHardPosterior Mean and VarianceMediumPrediction ParameterizationMediumClassifier-Free GuidanceMediumAnnealed Langevin DynamicsMediumGAN Training StepHardAutoencoder Forward PassMediumVariational Autoencoder Forward PassMedium

Accepts: array

Accepts: array

Accepts: array

Accepts: object

Accepts: integer

Accepts: number, null

You must run your code first.

Toy DDPM Sampler

Diffusion ModelsSamplingGenerative Models
Hard

Generate samples by running the full DDPM reverse process with a tiny noise-prediction network, for t = T - 1 down to 0.

x^0=xt−1−αˉt ϵ^αˉt,μ=βtαˉt−11−αˉt x^0+(1−αˉt−1)αt1−αˉt xtxt−1=μ+β~t  noise[T−1−t],β~t=βt(1−αˉt−1)1−αˉt\begin{aligned} \hat x_0 &= \frac{x_t - \sqrt{1 - \bar\alpha_t}\, \hat\epsilon}{\sqrt{\bar\alpha_t}}, \qquad \mu = \frac{\beta_t \sqrt{\bar\alpha_{t-1}}}{1 - \bar\alpha_t}\, \hat x_0 + \frac{(1 - \bar\alpha_{t-1}) \sqrt{\alpha_t}}{1 - \bar\alpha_t}\, x_t \\ x_{t-1} &= \mu + \sqrt{\tilde\beta_t}\; \text{noise}[T - 1 - t], \qquad \tilde\beta_t = \frac{\beta_t (1 - \bar\alpha_{t-1})}{1 - \bar\alpha_t} \end{aligned}x^0​xt−1​​=αˉt​​xt​−1−αˉt​​ϵ^​,μ=1−αˉt​βt​αˉt−1​​​x^0​+1−αˉt​(1−αˉt−1​)αt​​​xt​=μ+β~​t​​noise[T−1−t],β~​t​=1−αˉt​βt​(1−αˉt−1​)​​

Here βt\beta_tβt​ is betas[t], αt=1−βt\alpha_t = 1 - \beta_tαt​=1−βt​, αˉt=α0⋯αt\bar\alpha_t = \alpha_0 \cdots \alpha_tαˉt​=α0​⋯αt​ and αˉ−1=1\bar\alpha_{-1} = 1αˉ−1​=1. The loop starts from x_T, which is the sample at timestep T - 1, and step k = T - 1 - t uses noise[k]. At t = 0 the step returns μ\muμ with no noise. When clip_value is a number, x^0\hat x_0x^0​ is clamped to [-clip_value, clip_value] before μ\muμ is computed, and when it is None nothing is clamped.

The prediction is ϵ^=SiLU⁡([ xt,e(t) ]W1+b1)W2+b2\hat\epsilon = \operatorname{SiLU}([\,x_t, e(t)\,] W_1 + b_1) W_2 + b_2ϵ^=SiLU([xt​,e(t)]W1​+b1​)W2​+b2​ with params W1 (D + embed_dim, hidden), b1 (hidden), W2 (hidden, D), b2 (D) and SiLU⁡(z)=z⋅sigmoid⁡(z)\operatorname{SiLU}(z) = z \cdot \operatorname{sigmoid}(z)SiLU(z)=z⋅sigmoid(z). The embedding e(t)=[cos⁡(tf),sin⁡(tf)]e(t) = [\cos(t f), \sin(t f)]e(t)=[cos(tf),sin(tf)] uses half = embed_dim // 2 and fi=exp⁡(−ln⁡(10000) i/half)f_i = \exp(-\ln(10000)\, i / \text{half})fi​=exp(−ln(10000)i/half) for i<halfi < \text{half}i<half, plus one zero when embed_dim is odd, and is the same for all N rows.

Return a dict with "samples" (the final xxx, shape (N, D)) and "pred_x0" (shape (T, N, D), row k is the possibly clamped x^0\hat x_0x^0​ of step k), both with the dtype of x_T.

Examples

Input: x_T = [[1.0,-0.5],[0.2,0.8]], betas = [0.1,0.2,0.3], noise = [[[0.5,-1.0],[0.0,0.3]],[[-0.2,0.4],[1.0,-0.6]],[[0.7,0.7],[-0.7,0.1]]], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, clip_value = null

Output: {"samples":[[1.765787,-1.152671],[0.468101,0.89931]],"pred_x0":[[[1.594648,-0.79624],[0.289775,0.948434]],[[1.881834,-1.28839],[0.248787,1.053894]],[[1.765787,-1.152671],[0.468101,0.89931]]]}

Explanation: The schedule gives alpha_bars of 0.9, 0.72 and 0.504, and the loop visits t = 2, 1, 0 using noise[0] and noise[1]. The last step at t = 0 returns its posterior mean, which equals that step's predicted clean sample, so noise[2] is never used.

Input: x_T = [[2.0,-1.5],[-0.4,1.2],[0.0,0.0]], betas = [0.05,0.25,0.5,0.5], noise = [[[0.1,0.2],[-0.3,0.4],[0.5,-0.6]],[[1.0,0.0],[0.0,1.0],[-1.0,0.5]],[[0.3,-0.3],[0.2,0.2],[-0.4,0.1]],[[0.9,0.9],[0.9,0.9],[0.9,0.9]]], params = {"W1":[[0.6,-0.4],[0.2,0.5],[0.3,-0.1],[-0.2,0.4],[0.1,0.1]],"b1":[0.1,-0.1],"W2":[[0.7,-0.2],[0.3,0.6]],"b2":[0.0,0.0]}, embed_dim = 3, clip_value = 1.0

Output: {"samples":[[1.0,-1.0],[-1.0,1.0],[-0.040924,-0.336405]],"pred_x0":[[[1.0,-1.0],[-1.0,1.0],[0.131976,-0.079674]],[[1.0,-1.0],[-1.0,1.0],[0.64493,-0.670506]],[[1.0,-0.998199],[-1.0,1.0],[0.061717,-0.38619]],[[1.0,-1.0],[-1.0,1.0],[-0.040924,-0.336405]]]}

Input: x_T = [[0.5,-0.5]], betas = [0.2], noise = [[[3.0,-3.0]]], params = {"W1":[[0.5,-0.3,0.2],[0.1,0.4,-0.2],[0.3,0.0,0.1],[-0.2,0.2,0.5]],"b1":[0.0,0.1,-0.1],"W2":[[0.4,-0.1],[0.2,0.3],[-0.5,0.2]],"b2":[0.0,0.05]}, embed_dim = 2, clip_value = null

Output: {"samples":[[0.535208,-0.563034]],"pred_x0":[[[0.535208,-0.563034]]]}

Hint 1

Build alpha_bars once with torch.cumprod and treat the value before index 0 as 1.

Hint 2

Iterating with enumerate over range(T - 1, -1, -1) gives both the step index for noise and the timestep t.

Hint 3

torch.cat of the cos, sin and zero blocks builds one embedding row, and .expand(N, embed_dim) repeats it for every sample.

Hint 4

tensor.clamp(-c, c) clips values.

Requirements

  • Use PyTorch tensor operations; torch.nn.functional is allowed.
  • Use the posterior variance tilde beta for the noise scale, never betas[t].
  • Leave every input unchanged, including the tensors in params.

Constraints

  • x_T is a float tensor of shape (N, D) with N between 1 and 8 and D between 1 and 3.
  • betas is a float tensor of shape (T,) with T between 1 and 20 and values in [0.01, 0.5].
  • noise is a float tensor of shape (T, N, D).
  • params has exactly the keys W1 (D + embed_dim, hidden), b1 (hidden), W2 (hidden, D) and b2 (D), with hidden between 1 and 8.
  • embed_dim is an integer between 2 and 8, and clip_value is None or a positive number.
  • All float tensors share one dtype, float32 or float64, and all values are finite.
Try Similar Problems
DDIM StepHardDDPM Ancestral StepMediumKarras Euler SamplerHardDiT Patchify and adaLN-Zero BlockHardToy Diffusion Training StepHardPosterior Mean and VarianceMediumPrediction ParameterizationMediumClassifier-Free GuidanceMediumAnnealed Langevin DynamicsMediumGAN Training StepHardAutoencoder Forward PassMediumVariational Autoencoder Forward PassMedium

Accepts: array

Accepts: array

Accepts: array

Accepts: object

Accepts: integer

Accepts: number, null

You must run your code first.