Files
even-perfect-numbers-scanner/docs/HeuristicIsPrimePlan.md

159 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EvenPerfectBitScanner `--mersenne=bydivisor` Flow and Heuristic Prime Tester Plan
## 1. Execution Flow in `--mersenne=bydivisor` Mode
The following map captures every branch that executes when the scanner runs with `--mersenne=bydivisor`, highlighting interactions with prime testing, divisor heuristics, residue reuse, and factor verification.
1. **CLI processing and guards**
* `CliArguments.Parse` fills `_cliArguments`, then `Main` validates thread count, help flag, and shared GPU settings.
* `useByDivisor` is set from CLI. If enabled without `--filter-p` (and not in test mode), the scanner prints "--mersenne=bydivisor requires --filter-p=<path>." and exits early.【F:EvenPerfectBitScanner/Program.cs†L32-L132】
2. **Global configuration**
* GPU settings (`GpuPrimeWorkLimiter`, `PrimeTester.GpuBatchSize`, transforms) are configured for both CPU and GPU runs.
* By-divisor specific state records `_byDivisorStartPrime`, `_byDivisorTester`, and `_byDivisorPreviousResults` for later reuse.【F:EvenPerfectBitScanner/Program.cs†L134-L232】
3. **Tester initialization**
* When `useByDivisor` is active, `_byDivisorTester` is instantiated as GPU or CPU implementation, with `BatchSize` set from `--scan-batch`. `PrimeTester`/`MersenneNumberTester` pools are not created for this mode because the flow exits before the generic candidate loop.【F:EvenPerfectBitScanner/Program.cs†L200-L232】【F:EvenPerfectBitScanner/Program.cs†L400-L460】
4. **Results bootstrap**
* The scanner builds the results filename, initializes `CalculationResultHandler`, and loads previous results. Previous entries populate `_byDivisorPreviousResults` so the by-divisor runner can skip already processed primes.【F:EvenPerfectBitScanner/Program.cs†L232-L318】
5. **Candidate acquisition**
* `byDivisorCandidates` is populated either from deterministic test data (test mode) or by reloading the filter file with `CalculationResultsFile.LoadCandidatesWithinRange`. Limits (`--max-prime`) and skipped candidates are reported to the console.【F:EvenPerfectBitScanner/Program.cs†L318-L362】
* In contrast to other modes, generic residue filters and candidate queues are bypassed; only the provided prime list is considered.【F:EvenPerfectBitScanner/Program.cs†L318-L362】
6. **Previous-result pruning**
* `MersenneNumberDivisorByDivisorTester.Run` sorts the input primes, removes entries already present in `_byDivisorPreviousResults`, applies `--start-prime`, and converts the remaining list to spans for batch preparation.【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L11-L138】
7. **Tester configuration per maximum prime**
* `IMersenneNumberDivisorByDivisorTester.ConfigureFromMaxPrime` and `PrepareCandidates` pre-compute divisor limits for every prime. All primes with `allowedMax >= 3` are retained (current implementations never filter any out in production because limits are huge).【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L140-L201】
8. **Residue-sieved divisor scanning (CPU)**
* `MersenneNumberDivisorByDivisorCpuTester.CheckDivisors` initialises Montgomery stepping data (`GpuUInt128`), computes rolling residues for mod 10/8/5/3/7/11, and advances divisors by `2p` while applying decimal masks tied to the exponent’s last digit.【F:PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs†L63-L213】
* When a candidate passes residue filters, the CPU session obtains cycle lengths from `DivisorCycleCache` or `MersenneDivisorCycles`, validates them via Montgomery exponentiation, and reports hits while updating `_lastStatusDivisor`.【F:PerfectNumbers.Core/Cpu/MersenneNumberDivisorByDivisorCpuTester.cs†L213-L318】
9. **Residue-sieved divisor scanning (GPU)**
* `MersenneNumberDivisorByDivisorGpuTester.CheckDivisors` reuses the same wheel offsets and residue tests but performs them inside `DivisorByDivisorKernels.CheckKernel`, writing hits to pooled GPU buffers. CPU fallbacks honour the same residue, decimal mask, and cycle rules when GPU context is unavailable.【F:PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs†L1-L240】【F:PerfectNumbers.Core/Gpu/MersenneNumberDivisorByDivisorGpuTester.cs†L240-L520】
10. **Prime decision path**
* By-divisor sessions internally reuse divisor remainders across primes, but they **do not call `PrimeTester.IsPrime*`**. The primality decision depends entirely on divisor scans up to the limit configured for each prime candidate.
* If `IsPrime` returns `false`, the result file records `(detailedCheck=true, passedAllTests=false)`. When `true`, `divisorsExhausted` reports whether the scan reached the configured divisor bound (driving the `DetailedCheck` flag).【F:PerfectNumbers.Core/MersenneNumberDivisorByDivisorTester.cs†L203-L272】
11. **Shutdown**
* After `MersenneNumberDivisorByDivisorTester.Run` completes, the main loop flushes results and exits before any of the generic candidate enumeration logic runs.【F:EvenPerfectBitScanner/Program.cs†L400-L460】
## 2. Baseline Prime Tester Methods
The heuristics will replace the internal logic of the current GPU-assisted and CPU-only paths. The snapshots below capture the existing implementations before modification (copied verbatim for reference).
```csharp
public bool IsPrimeGpu(ulong n, CancellationToken ct)
{
bool forceCpu = GpuContextPool.ForceCpu;
Span<ulong> one = stackalloc ulong[1];
Span<byte> outFlags = stackalloc byte[1];
one[0] = n;
outFlags[0] = 0;
if (!forceCpu)
{
IsPrimeBatchGpu(one, outFlags);
}
bool belowGpuRange = n < 31UL;
bool gpuReportedPrime = !forceCpu && !belowGpuRange && outFlags[0] != 0;
bool requiresCpuFallback = forceCpu || belowGpuRange || !gpuReportedPrime;
return requiresCpuFallback ? IsPrimeCpu(n, ct) : true;
}
```
```csharp
internal static bool IsPrimeCpu(ulong n, CancellationToken ct)
{
bool isTwo = n == 2UL;
bool isOdd = (n & 1UL) != 0UL;
ulong mod5 = n % 5UL;
bool divisibleByFive = n > 5UL && mod5 == 0UL;
bool result = n >= 2UL && (isTwo || isOdd) && !divisibleByFive;
bool requiresTrialDivision = result && n >= 7UL && !isTwo;
if (requiresTrialDivision)
{
bool sharesMaxExponentFactor = n.Mod10() == 1UL && SharesFactorWithMaxExponent(n);
result &= !sharesMaxExponentFactor;
if (result)
{
var smallPrimeDivisorsLength = PrimesGenerator.SmallPrimes.Length;
uint[] smallPrimeDivisors = PrimesGenerator.SmallPrimes;
ulong[] smallPrimeDivisorsMul = PrimesGenerator.SmallPrimesPow2;
for (int i = 0; i < smallPrimeDivisorsLength; i++)
{
if (smallPrimeDivisorsMul[i] > n)
{
break;
}
if (n % smallPrimeDivisors[i] == 0)
{
result = false;
break;
}
}
}
}
return result;
}
```
## 3. Implementation Plan for `HeuristicPrimeTester`
The plan incorporates the updated divisor-class heuristics (Groups A/B, wheels, residue priorities) and connects them to both CPU and GPU execution, including every by-divisor integration point. Items already completed are marked with `[done]`.
1. [done] **Snapshot current implementations** – preserve `IsPrimeGpu` and `IsPrimeCpu` as references for regression comparisons and migration checkpoints (see Section 2).
2. [done] **Introduce placeholder heuristic entry points** – add `IsPrimeCpu` and `IsPrimeGpu` by copying the current internal and GPU methods so subsequent commits can iterate without disrupting legacy behavior.
3. [done] **Design shared heuristic scaffolding**
* [done] Added `HeuristicPrimeSieves` to precompute 4M-entry Group A/B divisor tables so heuristic prime checks reuse cached sequences without regenerating wheel steps at runtime; the enumerator remains available for residue-driven consumers.
* [done] Surface helpers that accept the precomputed `R = ⌊√n⌋` and `nMod10` values from callers via the new overload of `IsPrimeCpu`; HeuristicPrimeTester still computes them locally until call sites forward the parameters.
* [done] Simplified the heuristic execution path so it runs without cancellation tokens or callback plumbing while keeping the shared enumerator available to other components.
4. **Share residue and wheel state with by-divisor sessions**
* [done] Extracted the rolling-modulus helpers (mod 10/8/5/3/7/11) and decimal masks from `MersenneNumberDivisorByDivisorCpuTester.CheckDivisors` into the shared `MersenneDivisorResidueStepper` so heuristic prime tests and by-divisor sessions consume the same filters.
* [done] Audited how `MersenneCpuDivisorScanSession` advances residues via `ExponentRemainderStepperCpu`/`CycleRemainderStepper` and updated the GPU by-divisor path to reuse `MersenneDivisorResidueStepper`, keeping CPU/GPU residue stepping aligned.
* [done] Publish wheel iterators that can stream candidates directly into `DivisorCycleCache`, GPU hit buffers, or CPU Montgomery checks without recomputing offsets by exposing `HeuristicPrimeTester.CreateHeuristicDivisorEnumerator` and the reusable `HeuristicGroupBSequenceState` buffer.
5. **Expose cycle-preparation helpers**
* [done] Extend the heuristic scaffolding so it returns `MontgomeryDivisorData` descriptors and cycle-length hints for each candidate divisor via `HeuristicDivisorPreparation` and `PrepareHeuristicDivisor`.
* Wire these outputs into the existing cycle calculators to avoid duplicate cache lookups inside the CPU/GPU divisor scans.
6. [done] **Implement `IsPrimeCpu`**
* Baseline Group A/B enumeration now executes inside `IsPrimeCpu` via the cached `HeuristicPrimeSieves` tables, computing `⌊√n⌋` locally and short-circuiting on the first divisor.
* [done] The CPU heuristic now operates as a pure trial-division sweep without maintaining per-call summaries, relying on `PrepareHeuristicDivisor` only when downstream consumers request Montgomery data.
* [done] Introduced the temporary `UseHeuristicGroupBTrialDivision` gate so current builds execute Group A locally and then fall back to `Open.Numeric.Primes.Prime.Numbers.IsPrime`, keeping the full Group B implementation available for reactivation.
7. [done] **Implement `IsPrimeGpu`**
* [done] Mirror CPU structure while batching divisor checks on the accelerator via `HeuristicTrialDivisionGpu`, drawing divisors from `HeuristicPrimeSieves`, reusing caller-provided `sqrtLimit`/`nMod10` inputs, and falling back to CPU when GPUs are disabled.
* [done] Added `PrimeTesterKernels.HeuristicTrialDivisionKernel` so GPU batches respect Group A/B ordering while emitting hit flags for early exits.
* [done] Kept GPU divisibility in standard modular arithmetic while reserving Montgomery transforms for CPU confirmation logic.
* [done] When the temporary Group B gate is disabled, delegate GPU calls to the CPU fallback so Group A coverage remains in place while the accelerator path stays available for future re-enablement.
8. [done] **Refactor by-divisor CPU/GPU testers to reuse heuristics**
* [done] Drove `MersenneNumberDivisorByDivisorCpuTester.CheckDivisors` from `HeuristicPrimeTester.CreateMersenneDivisorEnumerator`, eliminating the bespoke residue loop and retiring the legacy status counter.
* [done] Streamed GPU batches from the same enumerator and Montgomery preparation data, removing the filtered-divisor scratch arrays while continuing to feed the existing kernel and hit bookkeeping.
9. [done] **Route legacy prime checks through heuristics**
* [done] Introduce `HeuristicPrimeTester` so heuristic CPU/GPU checks live alongside the legacy `PrimeTester` implementations while callers opt in explicitly.
* [done] Maintain legacy paths for benchmarking via `PrimeTester.IsPrimeCpu`, `PrimeTester.Exclusive.IsPrimeGpu`, and the explicit `HeuristicPrimeTester` entry points so benchmark harnesses can compare behaviours without runtime switches.
10. **Validation and benchmarking**
* Create targeted unit tests covering boundary cases for Group A/B transitions, wheel enumerations, residue deltas, and the stop-on-hit behaviour.
* Expand GPU test harnesses to assert equivalence between CPU/GPU heuristics on sampled composite and prime inputs and to verify the residue-stream contract with by-divisor sessions.
* Benchmark against historical data (hundreds of thousands of composites) to confirm that heuristic ordering improves early divisor detection and that residue sharing reduces duplicate computation.
* [done] Build BenchmarkDotNet comparisons for `HeuristicPrimeTester.IsPrimeGpu`, `HeuristicPrimeTester.IsPrimeCpu`, and `Open.Numeric.Primes.Prime.Numbers.IsPrime` across representative ranges (≤100, ≤4,000,000, and ≥138,000,000) before routing heuristic code into production. Implemented in `EvenPerfectBitScanner.Benchmarks/PrimeTesterBenchmarks.cs` to exercise both CPU and GPU heuristic paths alongside the external library.
11. **Documentation and rollout**
* Update README/docs to explain heuristic prerequisites (odd, not divisible by 5) and how configuration toggles affect execution across prime testing and by-divisor scans.
* Provide migration guidance for operators toggling between legacy and heuristic modes and describe how telemetry/metrics change after integration.
* Publish benchmark results in the heuristic specification once measurements are available so the documented guidance reflects the observed CPU/GPU/external comparisons.
## 4. Remaining Work Summary
* Complete the remaining rollout tasks in steps 10–11, including legacy-call-site integration, validation, and documentation updates.
* Develop comprehensive validation suites (unit tests and performance benchmarks) to compare heuristic behaviour with current implementations across both prime testing and by-divisor divisor scans.
* Document configuration switches, runtime metrics, and operational playbooks once the heuristics are in place.