Gliner/Neural/GlinerRelativePositions.cs
using System;
namespace GlinerPoc.Neural;
/// <summary>
/// Phase 4.17/4.18 — DeBERTa relative-position construction, exact port of
/// transformers 4.57.6 `build_relative_position` + `make_log_bucket_position`.
///
/// Pinned checkpoint: position_buckets = 256, max_relative_positions = -1.
/// Because bucketing only activates when bucket_size > 0 AND max_position > 0,
/// THIS CHECKPOINT USES RAW DIFFERENCES: matrix[q][k] = q - k, and the
/// attention path later offsets by `position_buckets` (256) to index the
/// 512-row rel embedding table: tableIndex = (q - k) + 256 ∈ [1, 511] for
/// sequences up to 256 tokens (guaranteed by the V1 length limit).
/// The log-bucket branch is implemented and unit-tested for completeness but
/// is not exercised by this checkpoint.
/// </summary>
public static class GlinerRelativePositions
{
/// <summary>Matrix [querySize, keySize] with matrix[q][k] = q - k (exact ints).</summary>
public static int[,] BuildRelativePositions( int querySize, int keySize, int bucketSize, int maxPosition )
{
if ( querySize <= 0 || keySize <= 0 )
{
throw new InvalidOperationException( "[GLI:ERROR] Relative positions require positive sizes." );
}
var result = new int[querySize, keySize];
if ( bucketSize > 0 && maxPosition > 0 )
{
for ( int q = 0; q < querySize; q++ )
{
for ( int k = 0; k < keySize; k++ )
{
result[q, k] = LogBucket( q - k, bucketSize, maxPosition );
}
}
}
else
{
for ( int q = 0; q < querySize; q++ )
{
for ( int k = 0; k < keySize; k++ )
{
result[q, k] = q - k;
}
}
}
return result;
}
/// <summary>
/// Exact port of make_log_bucket_position for a scalar. Torch computes:
/// sign = sign(x); mid = bucket//2;
/// abs_pos = mid-1 if (-mid < x < mid) else |x|;
/// log_pos = ceil( log(abs_pos/mid) / log((max_position-1)/mid) * (mid-1) ) + mid;
/// bucket = abs_pos <= mid ? x : log_pos * sign.
/// </summary>
public static int LogBucket( int relativePos, int bucketSize, int maxPosition )
{
int sign = Math.Sign( relativePos );
int mid = bucketSize / 2;
int absPos = (relativePos < mid && relativePos > -mid) ? mid - 1 : Math.Abs( relativePos );
double logPos = Math.Ceiling(
Math.Log( absPos / (double)mid ) / Math.Log( (maxPosition - 1) / (double)mid ) * (mid - 1) ) + mid;
bool useRaw = absPos <= mid;
return useRaw ? relativePos : (int)(logPos * sign);
}
/// <summary>Table index for the rel embedding lookup used by attention: (q-k) + span.</summary>
public static int ToTableIndex( int relativePos, int positionBucketSpan ) =>
relativePos + positionBucketSpan;
}