stack_dilations.py

7.2 kB · python · 190 lines

1from fractions import Fraction2from math import cos, gcd, log, pi, sqrt34import numpy as np56C = 2.0 * sqrt(2.0) / pi78def sine_coefficient(n):9    return C / n if n % 2 == 1 else 0.01011def dilate_coefficient(n, k):12    total = 0.013    for j in range(n):14        total += ((-1) ** j) * (cos(k * pi * j / n) - cos(k * pi * (j + 1) / n)) / (k * pi)15    return sqrt(2.0) * total1617def check_dilation_shift(scales, harmonics):18    worst = 0.019    for n in range(1, scales + 1):20        for k in range(1, harmonics + 1):21            want = sine_coefficient(k // n) if k % n == 0 else 0.022            worst = max(worst, abs(dilate_coefficient(n, k) - want))23    print("shift: <s(nx), e_k> equals a_{k/n} when n divides k and 0 otherwise, max deviation "24          "%.2e over n <= %d and k <= %d, so the dilate s(nx) carries the Dirichlet series n^-s S(s)"25          % (worst, scales, harmonics))2627def grid_integral(m, n):28    lcm = m * n // gcd(m, n)29    total = 030    for k in range(lcm):31        a = (m * (2 * k + 1)) // (2 * lcm)32        b = (n * (2 * k + 1)) // (2 * lcm)33        total += 1 if (a + b) % 2 == 0 else -134    return Fraction(total, lcm)3536def symbol_entry(m, n):37    g = gcd(m, n)38    if (m // g) % 2 == 1 and (n // g) % 2 == 1:39        return Fraction(g * g, m * n)40    return Fraction(0)4142def symbol_series(m, n, terms):43    g = gcd(m, n)44    mm, nn = m // g, n // g45    total = 0.046    for j in range(1, terms + 1):47        total += sine_coefficient(j * nn) * sine_coefficient(j * mm)48    return total4950def check_gram_two_ways(cap, terms):51    pairs = 052    worst = 0.053    for m in range(1, cap + 1):54        for n in range(m, cap + 1):55            exact = grid_integral(m, n)56            assert exact == symbol_entry(m, n), "gram entry (%d,%d)" % (m, n)57            g = gcd(m, n)58            slack = C * C / ((m // g) * (n // g) * 2.0 * terms)59            gap = abs(symbol_series(m, n, terms) - float(exact))60            assert gap <= slack + 1e-12, "symbol series (%d,%d)" % (m, n)61            worst = max(worst, gap)62            pairs += 163    print("gram: the L^2(0,1) integral of s(mx)s(nx) on the lcm grid equals the symbol sum "64          "sum_j a_{jn'} a_{jm'} at all %d pairs m <= n <= %d, exact in rationals, reading %s at "65          "(3,5), %s at (3,9) and %s at (2,3)"66          % (pairs, cap, grid_integral(3, 5), grid_integral(3, 9), grid_integral(2, 3)))67    print("gram: the truncated symbol sum at %d terms sits within its own tail bound of the closed "68          "form at every pair, largest gap %.2e" % (terms, worst))6970def check_blocks(cap):71    off = 072    for m in range(1, cap + 1):73        for n in range(1, cap + 1):74            e = symbol_entry(m, n)75            am, an = (m & -m).bit_length() - 1, (n & -n).bit_length() - 176            if am != an:77                assert e == 0, "block leak (%d,%d)" % (m, n)78                off += 179            else:80                mo, no = m >> am, n >> an81                assert e == Fraction(gcd(mo, no) ** 2, mo * no), "block value (%d,%d)" % (m, n)82    print("blocks: the Gram entry vanishes whenever v_2(m) differs from v_2(n), %d ordered pairs to "83          "%d, and the block at v_2 = a is the odd Gram itself, so the full dilation system's Gram "84          "is a countable direct sum of copies of the odd one" % (off, cap))8586def exact_det(rows):87    a = [row[:] for row in rows]88    n = len(a)89    det = Fraction(1)90    for i in range(n):91        p = next(r for r in range(i, n) if a[r][i] != 0)92        if p != i:93            a[i], a[p] = a[p], a[i]94            det = -det95        det *= a[i][i]96        inv = Fraction(1) / a[i][i]97        for r in range(i + 1, n):98            f = a[r][i] * inv99            if f:100                for c in range(i, n):101                    a[r][c] -= f * a[i][c]102    return det103104def smith_product(odds):105    want = Fraction(1)106    for k in odds:107        num, den, t, p = 1, 1, k, 3108        if t % 2 == 0:109            t //= 2110        while p * p <= t:111            if t % p == 0:112                num *= p * p - 1113                den *= p * p114                while t % p == 0:115                    t //= p116            p += 2117        if t > 1:118            num *= t * t - 1119            den *= t * t120        want *= Fraction(num, den)121    return want122123def check_determinant(cap):124    for k in range(1, cap + 1):125        odds = list(range(1, 2 * k, 2))126        rows = [[Fraction(gcd(a, b) ** 2, a * b) for b in odds] for a in odds]127        assert exact_det(rows) == smith_product(odds), "det at K=%d" % k128    odds = list(range(1, 26, 2))129    print("det: the odd Gram determinant equals prod over odd k of prod over p | k of (1 - p^-2) at "130          "every K = 1..%d, the value over the %d odd scales n <= 25 being %s, the number "131          "lab/py/stack-levels prints" % (cap, len(odds), smith_product(odds)))132133def check_inverse(cap, primecap):134    ok = 0135    for n in range(1, cap + 1):136        total = Fraction(0)137        for d in range(1, n + 1):138            if n % d == 0 and d % 2 == 1 and (n // d) % 2 == 1:139                mu, t, p, sq = 1, d, 3, False140                while p * p <= t:141                    if t % p == 0:142                        t //= p143                        if t % p == 0:144                            sq = True145                            break146                        mu = -mu147                    p += 2148                if sq:149                    continue150                if t > 1:151                    mu = -mu152                total += Fraction(mu, d) * Fraction(1, n // d)153        assert total == (1 if n == 1 else 0), "inverse at n=%d" % n154        ok += 1155    sieve = bytearray([1]) * (primecap + 1)156    sieve[0] = sieve[1] = 0157    for p in range(2, int(primecap ** 0.5) + 1):158        if sieve[p]:159            sieve[p * p::p] = bytearray(len(sieve[p * p::p]))160    tail = sum(C / p for p in range(3, primecap + 1, 2) if sieve[p])161    print("inverse: the Dirichlet inverse of a_n/a_1 is mu(n)/n on the odd n and 0 on the even, "162          "checked at every n <= %d, so 1/S(s) = 1/(a_1 (1 - 2^-1-s) zeta(1 + s)) and the Mobius "163          "square wave, coefficients mu(n) a_n, has symbol a_1^2/S(s)" % ok)164    print("inverse: a_n/a_1 and its inverse are totally multiplicative, and the Riesz test sum over "165          "the primes reads %.4f already at p <= %d, a divergent sum by Mertens, so neither system "166          "is a Riesz basis; the verdict is invariant under scaling phi by a_1" % (tail, primecap))167168def spectrum(caps):169    print("spectrum: K, top odd scale N, lambda_max, lambda_min, condition number, "170          "lambda_max/(log N)^2, lambda_max/(log log N)^2")171    for k in caps:172        odds = np.arange(1, 2 * k, 2, dtype=np.float64)173        g = np.gcd(odds.astype(np.int64)[:, None], odds.astype(np.int64)[None, :]).astype(np.float64)174        mat = g * g / (odds[:, None] * odds[None, :])175        ev = np.linalg.eigvalsh(mat)176        top, bot = ev[-1], ev[0]177        n = 2 * k - 1178        print("spectrum: %5d %6d %10.5f %12.3e %12.3e %8.4f %8.4f"179              % (k, n, top, bot, top / bot, top / (log(n) ** 2), top / (log(log(n)) ** 2)))180181def main():182    check_dilation_shift(8, 60)183    check_gram_two_ways(12, 20000)184    check_blocks(64)185    check_determinant(13)186    check_inverse(400, 100000)187    spectrum([25, 50, 100, 150, 200])188189if __name__ == "__main__":190    main()