File size: 3,480 Bytes
96b4304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""How often has each candidate digit actually been shown, so far and by the
end of the epoch.

Counts are a property of the stored pool: if digit d appears in c of puzzle p's
instances at that cell, then after P passes over the pair list the model has
seen (cell -> d) exactly c*P times. So the whole exposure picture follows from
the pool's per-(cell, candidate) count histogram times the pass count.
"""
import argparse

import numpy as np

D = "/tmp/sudoku_superposition"
CAND = "/tmp/sudoku_s12/train_cand_masks.npy"
STAGE = 0
BS = 64


def digits(m):
    return [d for d in range(1, 10) if int(m) & (1 << (d - 1))]


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--step", type=int, required=True)
    ap.add_argument("--puzzles", type=int, default=300)
    args = ap.parse_args()

    starts = np.load(f"{D}/train_starts.npy")
    counts = np.load(f"{D}/train_counts.npy")
    A = np.load(f"{D}/train_assignments.npy", mmap_mode="r")
    masks = np.load(CAND, mmap_mode="r")

    n_puz = counts.shape[0]
    pairs = int(np.asarray(counts[:, STAGE]).astype(np.int64).sum())
    per_pass = pairs / BS
    P = args.step / per_pass

    print(f"TRAIN  {n_puz:,} puzzles   stage-1 (puzzle, instance) pairs "
          f"{pairs:,}   {pairs / n_puz:.2f} instances/puzzle")
    print(f"one pass = {per_pass:,.0f} steps at batch {BS}; "
          f"one epoch = 5 passes = {5 * per_pass:,.0f} steps")
    print(f"step {args.step:,} -> {P:.2f} passes done "
          f"({P / 5 * 100:.1f}% of the epoch)")
    print(f"  each puzzle shown {P * pairs / n_puz:.1f} times so far, "
          f"{5 * pairs / n_puz:.1f} by the end")
    print(f"  each specific instance seen {P:.2f} times so far, 5 at the end")

    # per-(cell, candidate) counts inside the pool
    hist = np.zeros(8, dtype=np.int64)
    tot = 0
    ncand_sum = 0
    ncell = 0
    for q in range(args.puzzles):
        m = np.asarray(masks[q, STAGE])
        n = int(counts[q, STAGE])
        blk = np.asarray(A[starts[q, STAGE]:starts[q, STAGE] + n])
        for c in range(81):
            S = digits(m[c])
            if len(S) < 2:
                continue
            ncell += 1
            ncand_sum += len(S)
            v, k = np.unique(blk[:, c], return_counts=True)
            cnt = {int(a): int(b) for a, b in zip(v, k)}
            for d in S:
                hist[min(cnt.get(d, 0), 7)] += 1
                tot += 1

    print(f"\nover {args.puzzles} puzzles, {ncell:,} multi-candidate cells, "
          f"mean |S| {ncand_sum / ncell:.2f}")
    print(f"how many of the puzzle's ~6 instances carry each candidate:")
    print(f"  {'times in pool':>14}  {'share of (cell,cand)':>21}  "
          f"{'seen so far':>12}  {'seen by epoch end':>18}")
    for c in range(8):
        lab = f"{c}" if c < 7 else "7+"
        print(f"  {lab:>14}  {hist[c] / tot * 100:>20.1f}%  "
              f"{c * P:>12.1f}  {c * 5:>18}")
    mean_c = float((np.arange(8) * hist).sum() / tot)
    print(f"\n  mean over candidates: {mean_c:.2f} instances carry it, so "
          f"~{mean_c * P:.1f} sightings so far, ~{mean_c * 5:.1f} by epoch end")
    print(f"  never shown (0 in pool): {hist[0] / tot * 100:.1f}% of "
          f"(cell, candidate) pairs -- stays 0 forever")

    tst = np.load(f"{D}/test_counts.npy")
    print(f"\nVALIDATION  {tst.shape[0]:,} test puzzles available")
    print(f"  each eval uses eval_epochs=5 x batch {BS} = {5 * BS} puzzles")


if __name__ == "__main__":
    main()