Code/FlowSampler.cs
// C# adaptation of FlowHMR's x1 Euler sampling. See THIRD_PARTY_NOTICES.md.
using System;
using System.Threading;

namespace HumanoidMocapX;

/// <summary>Predicts the clean 491-channel motion latent at a normalized time in [0,1).
/// Writes every output element; failures and cancellation propagate.</summary>
public delegate void FlowPrediction(ReadOnlySpan<float> state, float time, Span<float> output, CancellationToken cancellation);

/// <summary>The released Base and Latest models use twenty Euler steps and x1 prediction.</summary>
public static class FlowSampler
{
    public const int Channels = 491;
    public const int Steps = 20;

    /// <summary>Replaces caller-supplied initial noise with sampled motion using a reusable
    /// prediction buffer. Rejects invalid shapes and non-finite predictions. A failed or
    /// cancelled run may leave state partially advanced; it must not be exported.</summary>
    public static void Sample(float[] state, float[] prediction, FlowPrediction predict,
        CancellationToken cancellation = default)
    {
        ArgumentNullException.ThrowIfNull(state);
        ArgumentNullException.ThrowIfNull(prediction);
        ArgumentNullException.ThrowIfNull(predict);
        if (state.Length == 0 || state.Length % Channels != 0 || state.Length / Channels > FlowMotion.MaximumFrames
            || prediction.Length != state.Length || ReferenceEquals(state, prediction))
            throw new ArgumentException("Sampling needs separate, equally sized [frames,491] state and prediction buffers.");
        CheckFinite(state);
        const float minimumRemaining = 1f / Steps;
        for (var step = 0; step < Steps; step++)
        {
            cancellation.ThrowIfCancellationRequested();
            var time = step / (float)Steps;
            var delta = (step + 1) / (float)Steps - time;
            predict(state, time, prediction, cancellation);
            cancellation.ThrowIfCancellationRequested();
            CheckFinite(prediction);
            var remaining = Math.Max(1f - time, minimumRemaining);
            for (var i = 0; i < state.Length; i++)
                state[i] += (prediction[i] - state[i]) / remaining * delta;
            CheckFinite(state);
        }
    }

    static void CheckFinite(ReadOnlySpan<float> values)
    {
        foreach (var value in values)
            if (!float.IsFinite(value)) throw new ArithmeticException("FlowHMR sampling produced or received a non-finite latent.");
    }
}