mertens_meter.py

3.2 kB · python · 115 lines

1import numpy as np234KNOWN = [5    14.1347,6    21.0220,7    25.0109,8    30.4249,9    32.9351,10    37.5862,11    40.9187,12    43.3271,13]1415N_MERTENS = 5000016N_STACK = 20017N_READOUT = 2000018N_SAMPLES = 819219BAND = (8.0, 55.0)20PEAK_FACTOR = 3.0212223def mobius(n):24    mu = np.ones(n + 1, dtype=np.int64)25    composite = np.zeros(n + 1, dtype=bool)26    for p in range(2, n + 1):27        if composite[p]:28            continue29        composite[p::p] = True30        mu[p::p] *= -131        square = p * p32        if square <= n:33            mu[square::square] = 034    mu[0] = 035    return mu363738def mertens(mu):39    running = np.cumsum(mu[1:].astype(np.int64))40    return np.concatenate((np.zeros(1, dtype=np.int64), running))414243def stack(mu, n):44    nodes = 045    brightness = np.empty(n, dtype=np.int64)46    for b in range(1, n + 1):47        brightness[b - 1] = mu[b::b][: n // b].sum()48        nodes += int((np.gcd(np.arange(b + 1), b) == 1).sum())49    return nodes, brightness505152def readout(m, n):53    breaches = 054    for x in range(1, n + 1):55        if int(m[x // np.arange(1, x + 1)].sum()) != 1:56            breaches += 157    return breaches585960def normalised(m, n):61    x = np.arange(1, n + 1, dtype=np.float64)62    return x, m[1 : n + 1] / np.sqrt(x)636465def spectrum(x, values, samples):66    log_x = np.log(x)67    grid = np.linspace(log_x[0], log_x[-1], samples)68    resampled = np.interp(grid, log_x, values)69    step = (grid[-1] - grid[0]) / (samples - 1)70    amplitude = np.fft.rfft(resampled * np.hanning(samples))71    gamma = np.fft.rfftfreq(samples, d=step) * 2.0 * np.pi72    return gamma, np.abs(amplitude) ** 2, step737475def peaks(gamma, power, band, factor):76    inside = (gamma > band[0]) & (gamma < band[1])77    threshold = np.median(power[inside]) * factor78    found = []79    for i in range(1, power.size - 1):80        if not inside[i]:81            continue82        if power[i] > power[i - 1] and power[i] > power[i + 1] and power[i] > threshold:83            found.append(i)84    return found, threshold858687def main():88    mu = mobius(N_MERTENS)89    m = mertens(mu)9091    nodes, brightness = stack(mu[: N_STACK + 1], N_STACK)92    depth = m[N_STACK // np.arange(1, N_STACK + 1)]93    agreements = int((brightness == depth).sum())94    breaches = readout(m, N_READOUT)9596    x, values = normalised(m, N_MERTENS)97    gamma, power, step = spectrum(x, values, N_SAMPLES)98    found, threshold = peaks(gamma, power, BAND, PEAK_FACTOR)99100    print(f"domain  N_mertens = {N_MERTENS}  N_stack = {N_STACK}")101    print(f"samples {N_SAMPLES}  log step {step:.8f}  bin width {gamma[1]:.6f}")102    print(f"stack   nodes {nodes}  brightness min {brightness.min()} max {brightness.max()}")103    print(f"stack   sum mu(kb) equals M(floor(N/b)) at {agreements} of {N_STACK} denominators")104    print(f"readout sum M(floor(x/n)) = 1 through x = {N_READOUT}, breaches {breaches}")105    print(f"band    {BAND[0]:g} to {BAND[1]:g}  peaks {len(found)}  threshold {threshold:.4g}")106    print(f"mertens M({N_MERTENS}) = {int(m[-1])}")107    print()108    print("known      detected  error")109    for target in KNOWN:110        best = min(found, key=lambda i: abs(gamma[i] - target))111        print(f"{target:8.4f}   {gamma[best]:7.2f}   {abs(gamma[best] - target):5.2f}")112113114if __name__ == "__main__":115    main()