BitContradiction GPU implementation fixes

This commit is contained in:
mlemanczyk committed 2026-01-25 19:04:56 +01:00
1 parent 17763033e1
commit e30c83584b
3 files changed
+195 -53

No files matched your search

@@ -11,6 +11,7 @@ using System.Globalization;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Buffers;
using ILGPU;
using ILGPU.Runtime;
using PerfectNumbers.Core.Gpu;
using PerfectNumbers.Core.Gpu.Kernels;
@@ -107,6 +108,9 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
int[] found = new int[maxThreadCount];
ulong[] batchIndexWords = new ulong[inputCapacity];
int debugWordsPerSlot = BitContradictionKernels.DebugWordCountPerSlot;
using MemoryBuffer1D<ulong, Stride1D.Dense> debugBuffer = gpu.Accelerator.Allocate1D<ulong>(maxThreadCount * debugWordsPerSlot);
ArrayView1D<ulong, Stride1D.Dense> debugView = debugBuffer.View;
BigInteger currentBatch = (currentK - BigInteger.One) / batchSizeBig;
currentK = (currentBatch * batchSizeBig) + BigInteger.One;
@@ -154,7 +158,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
gpu.InputView.CopyFromCPU(stream, batchIndexWords.AsSpan(0, threadCount * wordCount));
kernel(stream, threadCount, prime, gpu.InputView, countQ, gpu.OutputIntView);
kernel(stream, threadCount, prime, gpu.InputView, countQ, gpu.OutputIntView, debugView);
stream.Synchronize();
gpu.OutputIntView.CopyToCPU(stream, found.AsSpan(0, threadCount));
@@ -173,38 +177,79 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
}
BigInteger candidateQ = (step * candidateK) + BigInteger.One;
Span<ulong> debugQWords = stackalloc ulong[BitContradictionKernels.MaxQWordCount];
if (!BitContradictionKernels.TryBuildQWordsFromBatchIndex(prime, batchIndexWords.AsSpan(i * wordCount, wordCount), wordCount, foundIndex, debugQWords, out int debugQBitLen))
{
throw new InvalidOperationException($"BitContradiction GPU qWords overflow for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}");
}
BigInteger debugQ = ReadBigIntegerFromWords(debugQWords);
if (debugQ != candidateQ)
{
throw new InvalidOperationException($"BitContradiction GPU qWords mismatch for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}, cpuQ={candidateQ}, gpuQ={debugQ}, gpuQBits={debugQBitLen}");
}
#if DivisorSet_BitContradiction
int[]? qBits = _qBits;
if (qBits == null || qBits.Length < (int)prime)
{
qBits = new int[(int)prime];
_qBits = qBits;
}
bool cpuDecided = TryExactBitContradictionCheck(prime, candidateQ, candidateK, out bool cpuDivides);
if (!cpuDecided)
{
throw new InvalidOperationException($"BitContradiction CPU undecided for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
}
if (!cpuDivides)
{
throw new InvalidOperationException($"BitContradiction CPU/GPU mismatch for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
}
#endif
bool dividesExact = BigInteger.ModPow(2, prime, candidateQ) == BigInteger.One;
if (!dividesExact)
{
throw new InvalidOperationException($"BitContradiction CPU/GPU false positive for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
}
// Span<ulong> debugQWords = stackalloc ulong[BitContradictionKernels.MaxQWordCount];
// if (!BitContradictionKernels.TryBuildQWordsFromBatchIndex(prime, batchIndexWords.AsSpan(i * wordCount, wordCount), wordCount, foundIndex, debugQWords, out int debugQBitLen))
// {
// throw new InvalidOperationException($"BitContradiction GPU qWords overflow for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}");
// }
// BigInteger debugQ = ReadBigIntegerFromWords(debugQWords);
// if (debugQ != candidateQ)
// {
// throw new InvalidOperationException($"BitContradiction GPU qWords mismatch for p={prime}, k={candidateK}, batch={batchIndex}, index={foundIndex}, cpuQ={candidateQ}, gpuQ={debugQ}, gpuQBits={debugQBitLen}");
// }
// #if DivisorSet_BitContradiction
// int[]? qBits = _qBits;
// if (qBits == null || qBits.Length < (int)prime)
// {
// qBits = new int[(int)prime];
// _qBits = qBits;
// }
// bool cpuDecided = TryExactBitContradictionCheck(prime, candidateQ, candidateK, out bool cpuDivides);
// if (!cpuDecided)
// {
// throw new InvalidOperationException($"BitContradiction CPU undecided for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
// }
// if (!cpuDivides)
// {
// throw new InvalidOperationException($"BitContradiction CPU/GPU mismatch for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
// }
// #endif
// bool dividesExact = BigInteger.ModPow(2, prime, candidateQ) == BigInteger.One;
// if (!dividesExact)
// {
// throw new InvalidOperationException($"BitContradiction CPU/GPU false positive for p={prime}, q={candidateQ}, k={candidateK}, batch={batchIndex}");
if (prime == 138000001UL && candidateQ == new BigInteger(276000003))
{
ulong[] debugWords = new ulong[debugWordsPerSlot];
debugView.SubView(i * debugWordsPerSlot, debugWordsPerSlot).CopyToCPU(stream, debugWords);
BigInteger gpuQ = ReadBigIntegerFromWords(debugWords.AsSpan(0, 16));
BigInteger gpuInv = ReadBigIntegerFromWords(debugWords.AsSpan(16, 16));
BigInteger gpuALow = ReadBigIntegerFromWords(debugWords.AsSpan(32, 16));
ulong gpuQBits = debugWords[48];
ulong gpuQWordCount = debugWords[49];
ulong gpuQMask0 = debugWords[50];
ulong gpuQMask1 = debugWords[51];
ulong gpuKnownOne0 = debugWords[52];
ulong gpuKnownZero0 = debugWords[53];
ulong gpuAOneWin0 = debugWords[54];
ulong gpuAZeroWin0 = debugWords[55];
long gpuCarryMin = unchecked((long)debugWords[56]);
long gpuCarryMax = unchecked((long)debugWords[57]);
long gpuMaxAllowedA = unchecked((long)debugWords[58]);
long gpuMaxFixed = unchecked((long)debugWords[59]);
long gpuWindowSize = unchecked((long)debugWords[60]);
long gpuAWordCount = unchecked((long)debugWords[61]);
long gpuMaxKnownA = unchecked((long)debugWords[62]);
long gpuMaxFixedPrefilter = unchecked((long)debugWords[63]);
ulong preKnownOne0 = debugWords[64];
ulong preKnownZero0 = debugWords[65];
ulong preAOneWin0 = debugWords[66];
ulong preAZeroWin0 = debugWords[67];
long preCarryMin = unchecked((long)debugWords[68]);
long preCarryMax = unchecked((long)debugWords[69]);
long preSegmentBase = unchecked((long)debugWords[70]);
long preWindowColumn = unchecked((long)debugWords[71]);
long gpuOffsetCount = unchecked((long)debugWords[72]);
long gpuMaxOffset = unchecked((long)debugWords[73]);
long gpuFirstForced = unchecked((long)debugWords[74]);
long gpuFirstRemaining = unchecked((long)debugWords[75]);
long gpuFirstQWordsEff = unchecked((long)debugWords[76]);
ulong gpuFirstQLastEff = debugWords[77];
long gpuFirstCarryMin = unchecked((long)debugWords[78]);
long gpuFirstCarryMax = unchecked((long)debugWords[79]);
throw new InvalidOperationException($"BitContradiction GPU debug for p={prime}, q={candidateQ}, k={candidateK}, qBits={gpuQBits}, qWordCount={gpuQWordCount}, qMask0=0x{gpuQMask0:X16}, qMask1=0x{gpuQMask1:X16}, knownOne0=0x{gpuKnownOne0:X16}, knownZero0=0x{gpuKnownZero0:X16}, aOneWin0=0x{gpuAOneWin0:X16}, aZeroWin0=0x{gpuAZeroWin0:X16}, carryMin={gpuCarryMin}, carryMax={gpuCarryMax}, maxAllowedA={gpuMaxAllowedA}, maxFixed={gpuMaxFixed}, windowSize={gpuWindowSize}, aWordCount={gpuAWordCount}, maxKnownA={gpuMaxKnownA}, maxFixedPrefilter={gpuMaxFixedPrefilter}, preKnownOne0=0x{preKnownOne0:X16}, preKnownZero0=0x{preKnownZero0:X16}, preAOneWin0=0x{preAOneWin0:X16}, preAZeroWin0=0x{preAZeroWin0:X16}, preCarryMin={preCarryMin}, preCarryMax={preCarryMax}, preSegmentBase={preSegmentBase}, preWindowColumn={preWindowColumn}, offsetCount={gpuOffsetCount}, maxOffset={gpuMaxOffset}, firstForced={gpuFirstForced}, firstRemaining={gpuFirstRemaining}, firstQWordsEff={gpuFirstQWordsEff}, firstQLastEff=0x{gpuFirstQLastEff:X16}, firstCarryMin={gpuFirstCarryMin}, firstCarryMax={gpuFirstCarryMax}, gpuQ={gpuQ}, gpuInv={gpuInv}, gpuALow={gpuALow}");
}
foundDivisor = candidateQ;
AcceleratorStreamPool.Return(gpu.AcceleratorIndex, stream);
return true;
@@ -5201,3 +5246,7 @@ public struct MersenneNumberDivisorByDivisorCpuTesterWithTemplate() : IMersenneN
return false;
}
}