Initial BitTree implementation added.

This commit is contained in:
mlemanczyk committed 2026-01-09 16:13:16 +01:00
1 parent 45e3455ff3
commit d88ad25476
11 files changed
+3707 -257

No files matched your search

+88 -219
View File
@@ -14,6 +14,7 @@ namespace EvenPerfectBitScanner;
Additive, Additive,
TopDown, TopDown,
BitContradiction, BitContradiction,
BitTree,
} }
internal readonly struct CliArguments internal readonly struct CliArguments
@@ -279,6 +280,19 @@ namespace EvenPerfectBitScanner;
continue; continue;
} }
if (argument.Equals("--use-order", StringComparison.OrdinalIgnoreCase))
{
useOrder = true;
continue;
}
if (argument.StartsWith("--max-k=", StringComparison.OrdinalIgnoreCase))
{
maxKLimit = Utf8CliParser.ParseUInt64(argument.AsSpan("--max-k=".Length));
maxKConfigured = true;
continue;
}
if (argument.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase))
{ {
ReadOnlySpan<char> value = argument.AsSpan("--mersenne=".Length); ReadOnlySpan<char> value = argument.AsSpan("--mersenne=".Length);
@@ -318,15 +332,11 @@ namespace EvenPerfectBitScanner;
if (argument.StartsWith("--bydivisor-k-increment=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--bydivisor-k-increment=", StringComparison.OrdinalIgnoreCase))
{ {
string value = argument["--bydivisor-k-increment=".Length..].Trim(); ReadOnlySpan<char> value = argument.AsSpan("--bydivisor-k-increment=".Length);
if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase)) if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase))
{ {
byDivisorKIncrementMode = ByDivisorKIncrementMode.Pow2Groups; byDivisorKIncrementMode = ByDivisorKIncrementMode.Pow2Groups;
} }
else if (value.Equals("sequential", StringComparison.OrdinalIgnoreCase))
{
byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
}
else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase)) else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase))
{ {
byDivisorKIncrementMode = ByDivisorKIncrementMode.Predictive; byDivisorKIncrementMode = ByDivisorKIncrementMode.Predictive;
@@ -347,10 +357,13 @@ namespace EvenPerfectBitScanner;
{ {
byDivisorKIncrementMode = ByDivisorKIncrementMode.BitContradiction; byDivisorKIncrementMode = ByDivisorKIncrementMode.BitContradiction;
} }
else if (value.Equals("bittree", StringComparison.OrdinalIgnoreCase))
{
byDivisorKIncrementMode = ByDivisorKIncrementMode.BitTree;
}
else else
{ {
Console.WriteLine("Invalid value for --bydivisor-k-increment. Use sequential, pow2groups, predictive, percentile, additive, topdown, or bitcontradiction."); byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
return default;
} }
continue; continue;
@@ -358,98 +371,7 @@ namespace EvenPerfectBitScanner;
if (argument.StartsWith("--bydivisor-special-range=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--bydivisor-special-range=", StringComparison.OrdinalIgnoreCase))
{ {
if (Utf8CliParser.TryParseInt32(argument.AsSpan("--bydivisor-special-range=".Length), out int parsedRange) && parsedRange >= 0) byDivisorSpecialRange = Utf8CliParser.ParseInt32(argument.AsSpan("--bydivisor-special-range=".Length));
{
byDivisorSpecialRange = parsedRange;
}
else
{
Console.WriteLine("Invalid value for --bydivisor-special-range. Use a non-negative integer.");
return default;
}
continue;
}
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--divisor-cycles-limit=".Length), out ulong parsedLimit))
{
divisorCyclesSearchLimit = parsedLimit;
}
continue;
}
if (argument.StartsWith("--max-k=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--max-k=".Length), out ulong parsedMaxK))
{
maxKLimit = parsedMaxK;
maxKConfigured = true;
}
continue;
}
if (argument.StartsWith("--min-k=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> minKSpan = argument.AsSpan("--min-k=".Length);
if (Utf8CliParser.TryParseUInt64(minKSpan, out ulong parsedMinK) && parsedMinK > 0UL)
{
minK = parsedMinK;
}
else
{
try
{
BigInteger parsedMinKBig = BigInteger.Parse(minKSpan, CultureInfo.InvariantCulture);
if (parsedMinKBig > BigInteger.Zero)
{
minK = parsedMinKBig;
}
else
{
Console.WriteLine("Invalid value for --min-k.");
return default;
}
}
catch
{
Console.WriteLine("Invalid value for --min-k.");
return default;
}
}
continue;
}
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> value = argument.AsSpan("--mersenne-device=".Length);
mersenneDevice = ParseDevice(value);
continue;
}
if (argument.Equals("--test", StringComparison.OrdinalIgnoreCase))
{
testMode = true;
continue;
}
if (argument.Equals("--use-order", StringComparison.OrdinalIgnoreCase))
{
useOrder = true;
continue;
}
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--order-warmup-limit=".Length), out ulong parsedLimit))
{
orderWarmupLimitOverride = parsedLimit;
}
continue; continue;
} }
@@ -459,138 +381,30 @@ namespace EvenPerfectBitScanner;
continue; continue;
} }
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase)) if (argument.Equals("--test-mode", StringComparison.OrdinalIgnoreCase))
{ {
ReadOnlySpan<char> value = argument.AsSpan("--ntt=".Length); testMode = true;
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
continue; continue;
} }
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
{ {
ReadOnlySpan<char> value = argument.AsSpan("--mod-reduction=".Length); mersenneDevice = ParseDevice(argument.AsSpan("--mersenne-device=".Length));
if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.Mont64;
}
else if (value.Equals("barrett128", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.Barrett128;
}
else if (value.Equals("uint128", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.GpuUInt128;
}
else
{
modReductionMode = ModReductionMode.Auto;
}
continue; continue;
} }
if (argument.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase) ||
argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) ||
argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
{ {
forcePrimeKernelsOnCpu = argument.AsSpan("--primes-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase); ReadOnlySpan<char> value = argument.Contains('=') ? argument.AsSpan(argument.IndexOf('=') + 1) : argument.AsSpan();
forcePrimeKernelsOnCpu = value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
continue; continue;
} }
if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
{ {
ReadOnlySpan<char> value = argument.AsSpan("--order-device=".Length); orderDevice = ParseDevice(argument.AsSpan("--order-device=".Length));
orderDevice = ParseDevice(value);
continue;
}
if (argument.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase))
{
rleBlacklistPath = argument["--rle-blacklist=".Length..];
continue;
}
if (argument.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--rle-hard-max=".Length), out ulong parsedMax))
{
rleHardMaxP = parsedMax;
}
continue;
}
if (argument.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> value = argument.AsSpan("--rle-only-last7=".Length);
rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase);
continue;
}
if (argument.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase))
{
if (Utf8CliParser.TryParseDouble(argument.AsSpan("--zero-hard=".Length), out double zeroFraction))
{
zeroFractionHard = zeroFraction;
}
continue;
}
if (argument.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> value = argument.AsSpan("--zero-conj=".Length);
int colon = value.IndexOf(':');
if (colon > 0)
{
if (Utf8CliParser.TryParseDouble(value[..colon], out double zeroFrac) &&
Utf8CliParser.TryParseInt32(value[(colon + 1)..], out int maxZero))
{
zeroFractionConjecture = zeroFrac;
maxZeroConjecture = maxZero;
}
}
continue;
}
if (argument.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
{
mersenneDevice = ParseDevice(argument.AsSpan("--lucas=".Length));
continue;
}
if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase))
{
forcePrimeKernelsOnCpu = argument.AsSpan("--primes=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
continue;
}
if (argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase))
{
forcePrimeKernelsOnCpu = argument.AsSpan("--gpu-kernels=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
continue;
}
if (argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
{
forcePrimeKernelsOnCpu = argument.AsSpan("--accelerator=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
continue;
}
if (argument.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase))
{
resultsDirectory = argument["--results-dir=".Length..];
continue;
}
if (argument.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase))
{
resultsPrefix = argument["--results-prefix=".Length..];
continue;
}
if (argument.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase))
{
gpuPrimeThreads = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-threads=".Length)));
continue; continue;
} }
@@ -600,6 +414,12 @@ namespace EvenPerfectBitScanner;
continue; continue;
} }
if (argument.StartsWith("--gpu-prime-threads=", StringComparison.OrdinalIgnoreCase))
{
gpuPrimeThreads = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-threads=".Length)));
continue;
}
if (argument.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase))
{ {
sliceSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--ll-slice=".Length))); sliceSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--ll-slice=".Length)));
@@ -618,6 +438,54 @@ namespace EvenPerfectBitScanner;
continue; continue;
} }
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> value = argument.AsSpan("--mod-reduction=".Length);
if (value.Equals("uint128", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.GpuUInt128;
}
else if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.Mont64;
}
else if (value.Equals("barrett128", StringComparison.OrdinalIgnoreCase))
{
modReductionMode = ModReductionMode.Barrett128;
}
else
{
modReductionMode = ModReductionMode.Auto;
}
continue;
}
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
{
ReadOnlySpan<char> value = argument.AsSpan("--ntt=".Length);
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
continue;
}
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
{
orderWarmupLimitOverride = Utf8CliParser.ParseUInt64(argument.AsSpan("--order-warmup-limit=".Length));
continue;
}
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
{
divisorCyclesSearchLimit = Utf8CliParser.ParseUInt64(argument.AsSpan("--divisor-cycles-limit=".Length));
continue;
}
if (argument.StartsWith("--min-k=", StringComparison.OrdinalIgnoreCase))
{
minK = BigInteger.Parse(argument.AsSpan("--min-k=".Length), NumberStyles.None, CultureInfo.InvariantCulture);
continue;
}
if (argument.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase)) if (argument.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase))
{ {
filterFile = argument["--filter-p=".Length..]; filterFile = argument["--filter-p=".Length..];
@@ -651,6 +519,7 @@ namespace EvenPerfectBitScanner;
if (argument.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase)) if (argument.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase))
{ {
continueCyclesGeneration = true; continueCyclesGeneration = true;
continue;
} }
} }
@@ -715,14 +584,14 @@ namespace EvenPerfectBitScanner;
Console.WriteLine(" --increment=bit|add exponent increment method (default add)"); Console.WriteLine(" --increment=bit|add exponent increment method (default add)");
Console.WriteLine(" --threads=<value> number of worker threads"); Console.WriteLine(" --threads=<value> number of worker threads");
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method"); Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive|percentile|additive|topdown|bitcontradiction k iteration strategy for --mersenne=bydivisor (default sequential)"); Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive|percentile|additive|topdown|bitcontradiction|bittree k iteration strategy for --mersenne=bydivisor (default sequential)");
Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)"); Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)");
Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor"); Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor");
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)"); Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
Console.WriteLine(" --primes-device=cpu|gpu device for prime-scan kernels (default gpu)"); Console.WriteLine(" --primes-device=cpu|gpu device for prime-scan kernels (default gpu)");
Console.WriteLine(" --gpu-prime-batch=<n> batch size for GPU primality sieve (default 262144)");
Console.WriteLine($" --gpu-ratio=<n> ratio of GPU operations vs CPU in hybrid device mode (default {PerfectNumberConstants.GpuRatio})"); Console.WriteLine($" --gpu-ratio=<n> ratio of GPU operations vs CPU in hybrid device mode (default {PerfectNumberConstants.GpuRatio})");
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)"); Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
Console.WriteLine(" --gpu-prime-batch=<n> batch size for GPU primality sieve (default 262144)");
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)"); Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
Console.WriteLine(" --order-device=cpu|gpu|hybrid Device for order computations (default gpu)"); Console.WriteLine(" --order-device=cpu|gpu|hybrid Device for order computations (default gpu)");
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)"); Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
+68 -1
View File
@@ -32,6 +32,9 @@ using ByDivisorGpuOrderTopDown = PerfectNumbers.Core.Cpu.MersenneNumberDivisorBy
using ByDivisorCpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder; using ByDivisorCpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using ByDivisorHybridOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder; using ByDivisorHybridOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using ByDivisorGpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder; using ByDivisorGpuOrderBitContradiction = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
using ByDivisorCpuOrderBitTree = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder;
using ByDivisorHybridOrderBitTree = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForHybridOrder;
using ByDivisorGpuOrderBitTree = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForGpuOrder;
using System.Globalization; using System.Globalization;
namespace EvenPerfectBitScanner; namespace EvenPerfectBitScanner;
@@ -64,6 +67,9 @@ private static ThreadLocal<object>? MersenneTesters;
private static ByDivisorCpuOrderBitContradiction _byDivisorCpuOrderTesterBitContradiction; private static ByDivisorCpuOrderBitContradiction _byDivisorCpuOrderTesterBitContradiction;
private static ByDivisorHybridOrderBitContradiction _byDivisorHybridOrderTesterBitContradiction; private static ByDivisorHybridOrderBitContradiction _byDivisorHybridOrderTesterBitContradiction;
private static ByDivisorGpuOrderBitContradiction _byDivisorGpuOrderTesterBitContradiction; private static ByDivisorGpuOrderBitContradiction _byDivisorGpuOrderTesterBitContradiction;
private static ByDivisorCpuOrderBitTree _byDivisorCpuOrderTesterBitTree;
private static ByDivisorHybridOrderBitTree _byDivisorHybridOrderTesterBitTree;
private static ByDivisorGpuOrderBitTree _byDivisorGpuOrderTesterBitTree;
private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults; private static Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? _byDivisorPreviousResults;
private static string? _byDivisorModelPath; private static string? _byDivisorModelPath;
private static bool _byDivisorModeUsesModel; private static bool _byDivisorModeUsesModel;
@@ -335,6 +341,12 @@ private static ThreadLocal<object>? MersenneTesters;
case ByDivisorKIncrementMode.TopDown: case ByDivisorKIncrementMode.TopDown:
_byDivisorCpuOrderTesterTopDown = new ByDivisorCpuOrderTopDown(); _byDivisorCpuOrderTesterTopDown = new ByDivisorCpuOrderTopDown();
break; break;
case ByDivisorKIncrementMode.BitContradiction:
_byDivisorCpuOrderTesterBitContradiction = new ByDivisorCpuOrderBitContradiction();
break;
case ByDivisorKIncrementMode.BitTree:
_byDivisorCpuOrderTesterBitTree = new ByDivisorCpuOrderBitTree();
break;
default: default:
_byDivisorCpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder(); _byDivisorCpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
break; break;
@@ -359,6 +371,12 @@ private static ThreadLocal<object>? MersenneTesters;
case ByDivisorKIncrementMode.TopDown: case ByDivisorKIncrementMode.TopDown:
_byDivisorHybridOrderTesterTopDown = new ByDivisorHybridOrderTopDown(); _byDivisorHybridOrderTesterTopDown = new ByDivisorHybridOrderTopDown();
break; break;
case ByDivisorKIncrementMode.BitContradiction:
_byDivisorHybridOrderTesterBitContradiction = new ByDivisorHybridOrderBitContradiction();
break;
case ByDivisorKIncrementMode.BitTree:
_byDivisorHybridOrderTesterBitTree = new ByDivisorHybridOrderBitTree();
break;
default: default:
_byDivisorHybridOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder(); _byDivisorHybridOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithHybridOrder();
break; break;
@@ -383,6 +401,12 @@ private static ThreadLocal<object>? MersenneTesters;
case ByDivisorKIncrementMode.TopDown: case ByDivisorKIncrementMode.TopDown:
_byDivisorGpuOrderTesterTopDown = new ByDivisorGpuOrderTopDown(); _byDivisorGpuOrderTesterTopDown = new ByDivisorGpuOrderTopDown();
break; break;
case ByDivisorKIncrementMode.BitContradiction:
_byDivisorGpuOrderTesterBitContradiction = new ByDivisorGpuOrderBitContradiction();
break;
case ByDivisorKIncrementMode.BitTree:
_byDivisorGpuOrderTesterBitTree = new ByDivisorGpuOrderBitTree();
break;
default: default:
_byDivisorGpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder(); _byDivisorGpuOrderTester = new MersenneNumberDivisorByDivisorCpuTesterWithGpuOrder();
break; break;
@@ -545,6 +569,7 @@ private static ThreadLocal<object>? MersenneTesters;
ByDivisorKIncrementMode.Additive => "additive.kstate.faster", ByDivisorKIncrementMode.Additive => "additive.kstate.faster",
ByDivisorKIncrementMode.TopDown => "topdown.kstate.faster", ByDivisorKIncrementMode.TopDown => "topdown.kstate.faster",
ByDivisorKIncrementMode.BitContradiction => "bitcontradiction.kstate.faster", ByDivisorKIncrementMode.BitContradiction => "bitcontradiction.kstate.faster",
ByDivisorKIncrementMode.BitTree => "bittree.kstate.faster",
_ => "sequential.kstate.faster", _ => "sequential.kstate.faster",
}; };
string lmdbPath = Path.Combine(checksDir, Path.ChangeExtension(kRepoFile, ".lmdb")); string lmdbPath = Path.Combine(checksDir, Path.ChangeExtension(kRepoFile, ".lmdb"));
@@ -664,7 +689,7 @@ private static ThreadLocal<object>? MersenneTesters;
CalculationResultHandler.InitializeOutputBuffer(); CalculationResultHandler.InitializeOutputBuffer();
// Limit GPU concurrency only for prime checks (LL/NTT & GPU order scans). // Limit GPU concurrency only for prime checks (LL/NTT & GPU order scans).
GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads); GpuPrimeWorkLimiter.SetLimit(gpuPrimeThreads * PerfectNumberConstants.ThreadsByAccelerator);
// Configure batch size for GPU primality sieve // Configure batch size for GPU primality sieve
PrimeTester.GpuBatchSize = gpuPrimeBatch; PrimeTester.GpuBatchSize = gpuPrimeBatch;
@@ -751,6 +776,19 @@ private static ThreadLocal<object>? MersenneTesters;
threadCount, threadCount,
byDivisorPrimeRange); byDivisorPrimeRange);
break; break;
case ByDivisorKIncrementMode.BitTree:
Console.WriteLine("Using by-divisor bit-tree increment (CPU order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorCpuOrderTesterBitTree,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
break;
default: default:
MersenneNumberDivisorByDivisorTester.Run( MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates, byDivisorCandidates,
@@ -834,6 +872,19 @@ private static ThreadLocal<object>? MersenneTesters;
threadCount, threadCount,
byDivisorPrimeRange); byDivisorPrimeRange);
break; break;
case ByDivisorKIncrementMode.BitTree:
Console.WriteLine("Using by-divisor bit-tree increment (Hybrid order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorHybridOrderTesterBitTree,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
break;
default: default:
MersenneNumberDivisorByDivisorTester.Run( MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates, byDivisorCandidates,
@@ -917,6 +968,19 @@ private static ThreadLocal<object>? MersenneTesters;
threadCount, threadCount,
byDivisorPrimeRange); byDivisorPrimeRange);
break; break;
case ByDivisorKIncrementMode.BitTree:
Console.WriteLine("Using by-divisor bit-tree increment (GPU order).");
MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates,
ref _byDivisorGpuOrderTesterBitTree,
_byDivisorPreviousResults,
_byDivisorStartPrime,
static () => _lastCompositeP = true,
static () => _lastCompositeP = false,
HandleByDivisorResult,
threadCount,
byDivisorPrimeRange);
break;
default: default:
MersenneNumberDivisorByDivisorTester.Run( MersenneNumberDivisorByDivisorTester.Run(
byDivisorCandidates, byDivisorCandidates,
@@ -1327,6 +1391,7 @@ private static ThreadLocal<object>? MersenneTesters;
ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.Additive => _byDivisorCpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.TopDown => _byDivisorCpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorCpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.BitContradiction => _byDivisorCpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorCpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _), _ => _byDivisorCpuOrderTester.IsPrime(p, out detailedCheck, out _),
}; };
} }
@@ -1341,6 +1406,7 @@ private static ThreadLocal<object>? MersenneTesters;
ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.Additive => _byDivisorHybridOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.TopDown => _byDivisorHybridOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorHybridOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.BitContradiction => _byDivisorHybridOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorHybridOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _), _ => _byDivisorHybridOrderTester.IsPrime(p, out detailedCheck, out _),
}; };
} }
@@ -1353,6 +1419,7 @@ private static ThreadLocal<object>? MersenneTesters;
ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.Additive => _byDivisorGpuOrderTesterAdditive.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.TopDown => _byDivisorGpuOrderTesterTopDown.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitContradiction => _byDivisorGpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _), ByDivisorKIncrementMode.BitContradiction => _byDivisorGpuOrderTesterBitContradiction.IsPrime(p, out detailedCheck, out _),
ByDivisorKIncrementMode.BitTree => _byDivisorGpuOrderTesterBitTree.IsPrime(p, out detailedCheck, out _),
_ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _), _ => _byDivisorGpuOrderTester.IsPrime(p, out detailedCheck, out _),
}; };
} }
@@ -0,0 +1,43 @@
using System.Numerics;
using FluentAssertions;
using PerfectNumbers.Core.Cpu;
using Xunit;
namespace PerfectNumbers.Core.Tests;
public class BitTreeSolverTests
{
[Fact]
public void BitTree_FindsKnownDivisor()
{
// 2^11-1 = 2047 = 23 * 89, so k=1 yields divisor 23.
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder
{
DivisorLimit = new BigInteger(1000),
MinK = BigInteger.One,
};
bool isPrime = tester.IsPrime(11, out bool exhausted, out BigInteger divisor);
isPrime.Should().BeFalse();
exhausted.Should().BeTrue();
divisor.Should().Be(23);
}
[Fact]
public void BitTree_ReportsPrimeWhenNoDivisorFound()
{
// 2^5-1 = 31 is prime.
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder
{
DivisorLimit = new BigInteger(10_000),
MinK = BigInteger.One,
};
bool isPrime = tester.IsPrime(5, out bool exhausted, out BigInteger divisor);
isPrime.Should().BeTrue();
exhausted.Should().BeTrue();
divisor.Should().Be(BigInteger.Zero);
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -27,6 +27,10 @@ using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTeste
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder; using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder; using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder; using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
#elif DivisorSet_BitTree
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForHybridOrder;
using TesterGpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForGpuOrder;
#else #else
using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder; using TesterCpu = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder; using TesterHybrid = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForHybridOrder;
@@ -91,6 +95,8 @@ internal struct MersenneNumberDivisorByDivisorPrimeScanSessionWithTemplate
extension = ".topdown"; extension = ".topdown";
#elif DivisorSet_BitContradiction #elif DivisorSet_BitContradiction
extension = ".bitcontradiction"; extension = ".bitcontradiction";
#elif DivisorSet_BitTree
extension = ".bittree";
#endif #endif
string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension; string stateFile = prime.ToString(CultureInfo.InvariantCulture) + extension;
@@ -149,7 +149,7 @@ public sealed class PrimeOrderCalculatorAccelerator
private sealed class CalculatorKernels(Accelerator accelerator) private sealed class CalculatorKernels(Accelerator accelerator)
{ {
public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(PrimeOrderKernels.CalculateOrderKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>(); // public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>(PrimeOrderKernels.CalculateOrderKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers>>();
public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderKernels.CheckFactorsKernel)); public readonly Kernel CheckFactorsKernel = KernelUtil.GetKernel(accelerator.LoadStreamKernel<int, ulong, ArrayView1D<KeyValuePair<ulong, int>, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>>(PrimeOrderKernels.CheckFactorsKernel));
@@ -177,7 +177,7 @@ public sealed class PrimeOrderCalculatorAccelerator
public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>>(); public readonly Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>> SpecialMaxKernelLauncher = KernelUtil.GetKernel(accelerator.LoadAutoGroupedStreamKernel<Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>(PrimeOrderGpuHeuristics.EvaluateSpecialMaxCandidatesKernel)).CreateLauncherDelegate<Action<AcceleratorStream, Index1D, ulong, ArrayView<ulong>, int, ulong, ArrayView<ulong>>>();
} }
public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher; // public readonly Action<AcceleratorStream, Index1D, ulong, CalculateOrderKernelConfig, ulong, ulong, ulong, ulong, ulong, ArrayView1D<uint, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>, int, OrderKernelBuffers> CalculateOrderKernelLauncher;
public readonly Kernel CheckFactorsKernel; public readonly Kernel CheckFactorsKernel;
public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView1D<ulong, Stride1D.Dense>, ulong, ulong, ulong, ulong, ulong, ArrayView1D<ulong, Stride1D.Dense>> ConvertToStandardKernelLauncher;
public readonly Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>> HeuristicCombinedTrialDivisionKernelLauncher; public readonly Action<AcceleratorStream, Index1D, ArrayView<int>, ulong, ulong, ArrayView<ulong>, ArrayView<ulong>> HeuristicCombinedTrialDivisionKernelLauncher;
@@ -421,9 +421,9 @@ public sealed class PrimeOrderCalculatorAccelerator
DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View; DevicePrimesPow2LastThree = lastDigitSharedTables.DevicePrimesPow2LastThree.View;
DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View; DevicePrimesPow2LastNine = lastDigitSharedTables.DevicePrimesPow2LastNine.View;
DivisorTables = HeuristicCombinedGpuTables // DivisorTables = HeuristicCombinedGpuTables
.GetStaticTables(acceleratorIndex) // .GetStaticTables(acceleratorIndex)
.CreateViews(); // .CreateViews();
var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex); var smallPrimeFactorTables = SmallPrimeFactorGpuTables.GetStaticTables(acceleratorIndex);
@@ -436,7 +436,7 @@ public sealed class PrimeOrderCalculatorAccelerator
var kernels = _kernels[acceleratorIndex]; var kernels = _kernels[acceleratorIndex];
CalculateOrderKernelLauncher = kernels.CalculateOrderKernelLauncher; // CalculateOrderKernelLauncher = kernels.CalculateOrderKernelLauncher;
CheckFactorsKernel = kernels.CheckFactorsKernel; CheckFactorsKernel = kernels.CheckFactorsKernel;
ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher; ConvertToStandardKernelLauncher = kernels.ConvertToStandardKernelLauncher;
HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher; HeuristicCombinedTrialDivisionKernelLauncher = kernels.HeuristicCombinedTrialDivisionKernelLauncher;
@@ -272,27 +272,27 @@ internal static partial class PrimeOrderGpuHeuristics
var kernelConfig = new CalculateOrderKernelConfig(previousValue, hasPrevious, limit, config.MaxPowChecks, config.StrictMode); var kernelConfig = new CalculateOrderKernelConfig(previousValue, hasPrevious, limit, config.MaxPowChecks, config.StrictMode);
ref var buffers = ref gpu.CalculateOrderKernelBuffers; ref var buffers = ref gpu.CalculateOrderKernelBuffers;
var kernelLauncher = gpu.CalculateOrderKernelLauncher; // var kernelLauncher = gpu.CalculateOrderKernelLauncher;
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex); AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
// TODO: Remove the cleaning after the order kernel is modified to always set the result. // TODO: Remove the cleaning after the order kernel is modified to always set the result.
resultBufferView.MemSetToZero(stream); resultBufferView.MemSetToZero(stream);
statusBufferView.MemSetToZero(stream); statusBufferView.MemSetToZero(stream);
kernelLauncher( // kernelLauncher(
stream, // stream,
1, // 1,
prime, // prime,
kernelConfig, // kernelConfig,
divisorData.Modulus, // divisorData.Modulus,
divisorData.NPrime, // divisorData.NPrime,
divisorData.MontgomeryOne, // divisorData.MontgomeryOne,
divisorData.MontgomeryTwo, // divisorData.MontgomeryTwo,
divisorData.MontgomeryTwoSquared, // divisorData.MontgomeryTwoSquared,
smallPrimesView, // smallPrimesView,
smallSquaresView, // smallSquaresView,
(int)smallPrimesView.Length, // (int)smallPrimesView.Length,
buffers); // buffers);
byte status = 0; byte status = 0;
@@ -21,6 +21,9 @@ using MersenneNumberDivisorByDivisorCpuTesterWithAdditiveGpuOrder = PerfectNumbe
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder; using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder; using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder; using MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitContradictionDivisorSetForGpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForHybridOrder;
using MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForGpuOrder;
namespace PerfectNumbers.Core; namespace PerfectNumbers.Core;
@@ -88,6 +91,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) => Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr)); RunBitContradictionCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeCpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run( public static void Run(
List<ulong> candidates, List<ulong> candidates,
@@ -243,6 +277,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) => Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr)); RunBitContradictionHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeHybridOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run( public static void Run(
List<ulong> candidates, List<ulong> candidates,
@@ -398,6 +463,37 @@ public static class MersenneNumberDivisorByDivisorTester
Action<ulong, bool, bool, bool, BigInteger> pr) => Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitContradictionGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr)); RunBitContradictionGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run(
List<ulong> candidates,
ref MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder tester,
Dictionary<ulong, (bool DetailedCheck, bool PassedAllTests)>? previousResults,
ulong startPrime,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult,
int threadCount,
int primesPerTask)
=> RunStructTester(
candidates,
ref tester,
previousResults,
startPrime,
markComposite,
clearComposite,
printResult,
threadCount,
primesPerTask,
static (
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototype,
int workerCount,
int partitionSize,
Action mc,
Action cc,
Action<ulong, bool, bool, bool, BigInteger> pr) =>
RunBitTreeGpuOrderFiltered(filteredPrimes, prototype, workerCount, partitionSize, mc, cc, pr));
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void Run( public static void Run(
List<ulong> candidates, List<ulong> candidates,
@@ -2575,6 +2671,150 @@ public static class MersenneNumberDivisorByDivisorTester
})); }));
} }
private static void RunBitTreeCpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeCpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForCpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitTreeHybridOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeHybridOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForHybridOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitTreeGpuOrderFiltered(
List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototype,
int workerCount,
int partitionSize,
Action markComposite,
Action clearComposite,
Action<ulong, bool, bool, bool, BigInteger> printResult)
{
MersenneNumberDivisorByDivisorCpuTesterWithBitTreeGpuOrder prototypeCopy = prototype;
int totalCount = filteredPrimes.Count;
if (partitionSize > totalCount)
{
partitionSize = totalCount;
}
if (workerCount <= 1 || totalCount <= partitionSize)
{
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < totalCount; i++)
{
session.ProcessPrime(filteredPrimes[i]);
}
return;
}
var options = new ParallelOptions
{
MaxDegreeOfParallelism = workerCount,
};
Parallel.For(
0,
(int)Math.Ceiling((double)totalCount / partitionSize),
options,
new Action<int>(partitionIndex =>
{
int startIndex = partitionIndex * partitionSize;
int count = Math.Min(partitionSize, totalCount - startIndex);
var session = new Cpu.MersenneNumberDivisorByDivisorPrimeScanSessionWithForBitTreeDivisorSetForGpuOrder(prototypeCopy, markComposite, clearComposite, printResult);
for (int i = 0; i < count; i++)
{
session.ProcessPrime(filteredPrimes[startIndex + i]);
}
}));
}
private static void RunBitContradictionHybridOrderFiltered( private static void RunBitContradictionHybridOrderFiltered(
List<ulong> filteredPrimes, List<ulong> filteredPrimes,
in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototype, in MersenneNumberDivisorByDivisorCpuTesterWithBitContradictionHybridOrder prototype,
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Text;
using FASTER.core;
namespace PerfectNumbers.Core.Persistence;
/// <summary>
/// Stores BitTree frontier batches using Microsoft.FASTER.Core.
/// Each batch is a byte[] payload containing a sequence of serialized states.
/// Maintains a simple head/tail cursor stored inside the KV store.
/// </summary>
public sealed class BitTreeFrontierRepository : IDisposable
{
private readonly FasterKV<string, byte[]> _store;
private readonly IDevice _logDevice;
private readonly IDevice _objectLogDevice;
private long _headIndex;
private long _tailIndex;
private const string HeadKey = "__head__";
private const string TailKey = "__tail__";
public string DatabasePath { get; }
public static BitTreeFrontierRepository Open(string directory, string repositoryFileName = "bittree.frontier.faster")
{
Directory.CreateDirectory(directory);
string dbPath = Path.Combine(directory, repositoryFileName);
return new BitTreeFrontierRepository(dbPath);
}
private BitTreeFrontierRepository(string path)
{
DatabasePath = path;
_logDevice = Devices.CreateLogDevice(path + ".log");
_objectLogDevice = Devices.CreateLogDevice(path + ".obj.log");
_store = new FasterKV<string, byte[]>(
1L << 20,
new LogSettings
{
LogDevice = _logDevice,
ObjectLogDevice = _objectLogDevice,
});
LoadCursors();
}
public void AppendBatch(ReadOnlySpan<byte> batch)
{
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
string key = BuildChunkKey(_tailIndex);
session.Upsert(key, batch.ToArray());
_tailIndex++;
PersistCursors(session);
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
}
public bool TryDequeueBatch(int maxCount, out List<byte[]> batches)
{
batches = new List<byte[]>();
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
while (_headIndex < _tailIndex && batches.Count < maxCount)
{
string key = BuildChunkKey(_headIndex);
if (session.Read(key, out byte[]? stored).Found && stored is not null)
{
batches.Add(stored);
session.Delete(key);
}
_headIndex++;
}
if (batches.Count > 0)
{
PersistCursors(session);
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
return true;
}
session.CompletePending(true);
_store.Log.FlushAndEvict(true);
return false;
}
public void Dispose()
{
_store.Dispose();
_logDevice.Dispose();
_objectLogDevice.Dispose();
}
private void LoadCursors()
{
using var session = _store.For(new SimpleFunctions<string, byte[]>()).NewSession<SimpleFunctions<string, byte[]>>();
_headIndex = ReadCursor(session, HeadKey);
_tailIndex = ReadCursor(session, TailKey);
session.CompletePending(true);
}
private static long ReadCursor(ClientSession<string, byte[], byte[], byte[], Empty, SimpleFunctions<string, byte[]>> session, string key)
{
if (session.Read(key, out byte[]? stored).Found && stored is not null)
{
string text = Encoding.UTF8.GetString(stored);
if (long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out long value))
{
return value;
}
}
return 0;
}
private void PersistCursors(ClientSession<string, byte[], byte[], byte[], Empty, SimpleFunctions<string, byte[]>> session)
{
session.Upsert(HeadKey, Encoding.UTF8.GetBytes(_headIndex.ToString(CultureInfo.InvariantCulture)));
session.Upsert(TailKey, Encoding.UTF8.GetBytes(_tailIndex.ToString(CultureInfo.InvariantCulture)));
}
private static string BuildChunkKey(long index) => $"chunk-{index}";
}
+52
View File
@@ -0,0 +1,52 @@
# Bit-Tree Divisor Scan
Plan and notes for the `DivisorSet.BitTree` / `--bydivisor-k-increment=bittree` mode.
## Goal
- Divisor search via joint reconstruction of unknown bits in both the divisor \(q\) (only LSB=1 is known initially) and the cofactor \(a\), matching column-wise carries in \(q \cdot a = 2^p - 1\).
- Explore the full bit tree (all admissible \(q,a\) combinations) with early pruning from parity reachability, carry budgets, and length limits; no recursion (explicit stack/queue).
- Report primes \(p\) whose entire search space exhausts with no successful branch first.
## Operational assumptions
- \(p\) is odd; we target prime-like divisors with \(q \equiv 1 \pmod{2p}\) but start from bit patterns (q LSB = 1) and let the tree explore.
- Work in joint (a,q) space, not tied to a single \(k\). All admissible \(q\) consistent with \(2pk+1\) should be reachable through the bit tree.
- Iterative DFS/BFS (explicit stack/queue), no recursion. Process columns streaming (no full 138M+ bit buffers).
## Implementation steps
- [x] Add `BitTree` mode to CLI (`--bydivisor-k-increment`) and state repositories/files; ensure distinct state extensions.
- [x] Wire mode through CPU/Hybrid/GPU paths with `.bittree` state files.
- [x] Implement core bit-tree solver (frontier/BFS variant):
- Joint state: column index, carry, partial `q` (LSB=1), partial `a`, bounds on `a` length (≤ p - bitlen(q)+1), checks against `allowedMax`.
- Branching: choose next `q` bit (0/1), derive `a` bit from parity+carry; prune on length overflow, parity unreachable, carry cannot collapse, or `q` beyond allowedMax.
- Termination: success iff at/beyond column `p` with carry=0 and no further contributions from `(a,q)`; otherwise contradiction.
- [x] Integrate BitTree solver into `CheckDivisorsBitTree`; legacy per-`k` loop removed for this mode.
- [ ] Logging: emit Decided/Divides and contradiction reasons; throw if solver cannot decide.
- [ ] Tests:
- Unit tests on small \(p\) for success/failure paths.
- Integration test selecting `DivisorSet.BitTree` to ensure composites are detected and primes pass.
- [ ] Docs/help: mention the new mode in help text and README references.
## Variants considered
### Variant A (implemented baseline)
- Explicit stack-based DFS over `(a,q)` bits with reachability/pruning on parity and carry budgets.
- Processes columns sequentially without recursion; suited to small/medium `p` in current form.
### Variant B (frontier/windowed exploration — in progress)
- Implemented: frontier/BFS over branches with sliding window (suffix) storage for `(a,q)` and per-branch carry, bounded by `allowedMax` and `p`; window slides forward to cap memory and drop obsolete history.
- Planned enhancement: tune window size/adaptive policy and add optional frontier compression (baseline/delta) for `p ≥ 138M` and many threads.
### Variant C (frontier with shared baseline/deltas — planned)
- For each processed column maintain a shared baseline state (e.g., minimal carry/bit sums across the frontier).
- Each branch stores only its delta relative to the baseline (carry delta and the minimal differing `(a,q)` suffix), shrinking per-branch memory.
- When compressing the frontier after a window, pick the baseline that minimises aggregate delta size (e.g., minimal sum of carries/bit-counts across surviving branches).
- Objective: reduce memory further for highly parallel runs (many threads) while preserving full branch coverage.
## Current status
- Variant A solver is integrated and invoked from `DivisorSet.BitTree` (branch scan path); legacy per-`k` loop is disabled for this mode.
- Variant B is not implemented yet; design notes above.
## Next steps
- Implement frontier/windowed solver (Variant B) and add mode toggle if needed.
- Prototype baseline/delta compression for the frontier (Variant C) and measure memory savings under parallel load.
- Expand tests to cover both variants on small `p` and ensure integration keeps reporting decisions for all candidates.