Editor/Inference/FlowConditioning.cs
// C# adaptation of FlowHMR's timestep embedding. See THIRD_PARTY_NOTICES.md.
using System;
using System.Threading;

namespace HumanoidMocapX.Inference;

internal static class FlowConditioning
{
    internal static void Silu(Span<float> values)
    {
        for (var i = 0; i < values.Length; i++) values[i] /= 1 + MathF.Exp(-values[i]);
    }

    internal static void Gelu(Span<float> values)
    {
        const float sqrtTwoOverPi = .7978845608028654f, cubicCoefficient = .044715f;
        for (var i = 0; i < values.Length; i++)
        {
            var x = values[i];
            values[i] = .5f * x * (1 + MathF.Tanh(sqrtTwoOverPi * (x + cubicCoefficient * x * x * x)));
        }
    }

    internal static void Modulate(Span<float> values, ReadOnlySpan<float> shift, ReadOnlySpan<float> scale)
    {
        if (shift.Length == 0 || shift.Length != scale.Length || values.Length % shift.Length != 0)
            throw new ArgumentException("Modulation needs complete rows and equal shift/scale vectors.");
        for (var i = 0; i < values.Length; i++) values[i] = values[i] * (1 + scale[i % scale.Length]) + shift[i % shift.Length];
    }

    internal static void Time(float time, float factor, Span<float> output)
    {
        if (!float.IsFinite(time) || !float.IsFinite(factor) || output.Length < 2)
            throw new ArgumentException("Time embedding needs a finite time/factor and at least two channels.");
        var half = output.Length / 2;
        for (var i = 0; i < half; i++)
        {
            var angle = time * factor * MathF.Exp(-MathF.Log(10000f) * i / half);
            output[i] = MathF.Cos(angle); output[half + i] = MathF.Sin(angle);
        }
        if (output.Length % 2 != 0) output[^1] = 0;
    }

    internal static void AddGated(Span<float> values, ReadOnlySpan<float> delta, ReadOnlySpan<float> gate)
    {
        if (gate.Length == 0 || values.Length % gate.Length != 0 || delta.Length != values.Length)
            throw new ArgumentException("Gated residual needs equal [tokens,width] tensors and a [width] gate.");
        for (var i = 0; i < values.Length; i++) values[i] += delta[i] * gate[i % gate.Length];
    }
}

/// <summary>Two linear layers separated by SiLU, with reusable intermediate storage.</summary>
internal sealed class FlowConditionMlp
{
    readonly FlowLinear first, second;
    readonly float[] hidden;

    internal FlowConditionMlp(FlowWeights weights, string name, int inputs, int width)
    {
        first = new(weights, name + ".0", inputs, width);
        second = new(weights, name + ".2", width, width);
        hidden = new float[width];
    }

    internal void Run(ReadOnlySpan<float> input, Span<float> output, CancellationToken cancellation)
    {
        first.Run(input, 1, hidden, cancellation);
        FlowConditioning.Silu(hidden);
        second.Run(hidden, 1, output, cancellation);
    }
}