The DDPM (Ho et al. 2020, arXiv:2006.11239) linear variance schedule plus the two tensor operations defined directly on top of it – closed-form forward noising and the reverse (sampling) step.
More...
#include <noise_schedule.hpp>
|
| | NoiseSchedule (int64_t num_timesteps, float beta_start=1e-4f, float beta_end=0.02f) |
| | Precomputes the linear beta schedule and its alpha / alpha_bar derivatives.
|
| |
| int64_t | num_timesteps () const |
| | Number of diffusion steps T this schedule was built for.
|
| |
| float | beta (int64_t t) const |
| | Variance beta_t at timestep t.
|
| |
| float | alpha (int64_t t) const |
| | alpha_t = 1 - beta_t.
|
| |
| float | alpha_bar (int64_t t) const |
| | alpha_bar_t = prod_{s=1..t} alpha_s (the cumulative product).
|
| |
| Tensor | add_noise (const Tensor &x0, const Tensor &epsilon, int64_t t) const |
| | Closed-form forward (noising) sample: x_t = sqrt(alpha_bar_t) * x0 + sqrt(1 - alpha_bar_t) * epsilon.
|
| |
| Tensor | denoise_step (const Tensor &x_t, const Tensor &predicted_epsilon, const Tensor &z, int64_t t) const |
| | One reverse (sampling) step, x_{t-1} = mean_term + sqrt(beta_t) * z where the mean term is (1/sqrt(alpha_t)) * (x_t - (beta_t/sqrt(1-alpha_bar_t)) * eps_theta).
|
| |
The DDPM (Ho et al. 2020, arXiv:2006.11239) linear variance schedule plus the two tensor operations defined directly on top of it – closed-form forward noising and the reverse (sampling) step.
beta_t is linearly spaced over t = 1 .. T from beta_start to beta_end (the paper's own choice of schedule), with alpha_t = 1 - beta_t and alpha_bar_t = prod_{s=1..t} alpha_s precomputed once at construction.
- Note
- 1-indexed timesteps.
t runs over [1, T], matching the published math verbatim, not the 0-based indexing of the underlying storage. Every accessor takes the published t and does the offset internally; nothing outside this class ever sees the 0-based index.
-
Not a Module, and has no parameters. Nothing here is learned and nothing here has a backward pass of its own – the schedule is a fixed, deterministic function of its three constructor arguments, precomputed at construction in the same spirit as RoPEModule's fixed non-learned rotation angles. The training objective a diffusion model is actually optimized against is a plain MSELoss between the true and predicted noise (
L_simple), which needs no new loss class; the gradient path runs entirely through the caller's epsilon_theta network, never through this object.
-
No LRP rule, nothing to stub. Diffusion's research spike found no credible native LRP rule (iterative stochastic denoising has no single conserved relevance seed per timestep) – and since this is not a Module, there is no
propagate_relevance to stub either. Same disposition as Reparameterize / KLDivergenceLoss; see the campaign's Phase 5 Amendment (2026-09-23).
-
**
epsilon and z are caller-supplied, never sampled internally.** Identical convention to Reparameterize's epsilon: it keeps both operations deterministic and therefore directly testable against hand-computed values. Wiring real Gaussian sampling in is a demo's job, not this class's.
◆ NoiseSchedule()
| pulsatrix::NoiseSchedule::NoiseSchedule |
( |
int64_t |
num_timesteps, |
|
|
float |
beta_start = 1e-4f, |
|
|
float |
beta_end = 0.02f |
|
) |
| |
|
explicit |
Precomputes the linear beta schedule and its alpha / alpha_bar derivatives.
- Parameters
-
| num_timesteps | Number of diffusion steps T. Must be positive. |
| beta_start | Variance at t = 1. |
| beta_end | Variance at t = T. Must be strictly greater than beta_start. |
- Exceptions
-
| std::invalid_argument | if num_timesteps <= 0 or beta_start >= beta_end – external boundary (constructor arguments can originate from the Python bindings with no upstream validation), same convention as every module constructor's argument checks. |
- Note
- T == 1 is a degenerate but legal schedule: there is no interval to interpolate across, so
beta_1 = beta_start (using the linear formula's own t = 1 endpoint rather than dividing by T - 1 == 0).
◆ add_noise()
| Tensor pulsatrix::NoiseSchedule::add_noise |
( |
const Tensor & |
x0, |
|
|
const Tensor & |
epsilon, |
|
|
int64_t |
t |
|
) |
| const |
Closed-form forward (noising) sample: x_t = sqrt(alpha_bar_t) * x0 + sqrt(1 - alpha_bar_t) * epsilon.
The closed form is why a diffusion model can be trained on a randomly chosen t without ever simulating the intermediate steps 1..t-1.
- Parameters
-
| x0 | Clean data tensor. |
| epsilon | Caller-supplied noise, nominally ~ N(0, I). Must match x0's shape. |
| t | Timestep in [1, T]. |
- Returns
- The noised tensor x_t, same shape as x0.
- Exceptions
-
| std::invalid_argument | if x0 and epsilon have different shapes – external boundary, same classification as MSELoss::forward's shape check. |
| std::out_of_range | if t is outside [1, T]. |
- Note
- Device-generic: runs on Cpu, Cuda or Hip tensors (GPU-native-kernels Mission 1b); inputs must share one device.
◆ alpha()
| float pulsatrix::NoiseSchedule::alpha |
( |
int64_t |
t | ) |
const |
alpha_t = 1 - beta_t.
- Parameters
-
- Exceptions
-
| std::out_of_range | if t is outside [1, T]. |
◆ alpha_bar()
| float pulsatrix::NoiseSchedule::alpha_bar |
( |
int64_t |
t | ) |
const |
alpha_bar_t = prod_{s=1..t} alpha_s (the cumulative product).
- Parameters
-
- Exceptions
-
| std::out_of_range | if t is outside [1, T]. |
◆ beta()
| float pulsatrix::NoiseSchedule::beta |
( |
int64_t |
t | ) |
const |
Variance beta_t at timestep t.
- Parameters
-
- Exceptions
-
| std::out_of_range | if t is outside [1, T]. |
◆ denoise_step()
| Tensor pulsatrix::NoiseSchedule::denoise_step |
( |
const Tensor & |
x_t, |
|
|
const Tensor & |
predicted_epsilon, |
|
|
const Tensor & |
z, |
|
|
int64_t |
t |
|
) |
| const |
One reverse (sampling) step, x_{t-1} = mean_term + sqrt(beta_t) * z where the mean term is (1/sqrt(alpha_t)) * (x_t - (beta_t/sqrt(1-alpha_bar_t)) * eps_theta).
- Parameters
-
| x_t | Current noisy sample. |
| predicted_epsilon | The network's noise prediction at this timestep. Must match x_t's shape. |
| z | Caller-supplied noise, nominally ~ N(0, I) for t > 1 and exactly zero at t == 1 (the final step is deterministic in the published algorithm). Must match x_t's shape. Zeroing z at t == 1 is the caller's responsibility and is deliberately not enforced here – consistent with every other sampled input in this mission being a caller-supplied deterministic value rather than something this class decides. |
| t | Timestep in [1, T]. |
- Returns
- x_{t-1}, same shape as x_t.
- Exceptions
-
| std::invalid_argument | if the three shapes don't all match. |
| std::out_of_range | if t is outside [1, T]. |
- Note
- Same raw-host-loop device guard rationale as add_noise().
◆ num_timesteps()
| int64_t pulsatrix::NoiseSchedule::num_timesteps |
( |
| ) |
const |
|
inline |
Number of diffusion steps T this schedule was built for.
The documentation for this class was generated from the following file: