File size: 4,019 Bytes
bb23b91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
"""Worked example of superposition-instance generation for one puzzle/stage.

Prints the candidate sets, the dependencies read off the mask, the instances that
survived, the ones that were ruled out, and the resulting per-cell coverage.
Display is restricted to one unit so the table is readable; generation always
runs on the full grid.
"""
import argparse

import numpy as np

import superposition_instances as SI

NAMES = ([f"row {r}" for r in range(9)] + [f"col {c}" for c in range(9)]
         + [f"box {b}" for b in range(9)])


def fmt(cells):
    return "{" + ",".join(str(d) for d in cells) + "}"


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--masks", default="datasets_multicandidate_s12/test_cand_masks.npy")
    ap.add_argument("--puzzle", type=int, default=2)
    ap.add_argument("--stage", type=int, default=0)
    ap.add_argument("--unit", type=int, default=14, help="unit index to display (14 = col 5)")
    ap.add_argument("--max-confine", type=int, default=3)
    ap.add_argument("--max-instances", type=int, default=16)
    args = ap.parse_args()

    masks = np.load(args.masks, mmap_mode="r")
    mask = np.array(masks[args.puzzle, args.stage]).astype(np.uint16)

    unit = SI.UNITS[args.unit]
    forced, empties = SI.split_cells(mask)

    print("=" * 78)
    print(f"puzzle {args.puzzle}, stage {args.stage}, displaying {NAMES[args.unit]}")
    print("=" * 78)
    print(f"grid: {len(forced)} determined cells, {len(empties)} undetermined, "
          f"mean candidate count {np.mean([len(SI.digits_of(mask[c])) for c in empties]):.2f}")

    print(f"\ncandidate sets in {NAMES[args.unit]}:")
    for c in unit:
        r, cc = divmod(c, 9)
        ds = SI.digits_of(mask[c])
        tag = "determined" if len(ds) == 1 else ""
        print(f"    r{r}c{cc}  {fmt(ds):<22} {tag}")

    all_deps = SI.dependencies_from_mask(mask, max_confine=9)
    conf_deps = SI.dependencies_from_mask(mask, max_confine=args.max_confine)
    print(f"\ndependencies over the whole grid:")
    print(f"    {len(all_deps)} total (every unit, every unplaced digit)")
    print(f"    {len(conf_deps)} with confinement size <= {args.max_confine} "
          f"(these are the ones enforced)")
    sizes = {}
    for _, S in all_deps:
        sizes[len(S)] = sizes.get(len(S), 0) + 1
    print("    by confinement size: "
          + ", ".join(f"|S|={k}: {v}" for k, v in sorted(sizes.items())))

    print(f"\ndependencies enforced inside {NAMES[args.unit]}:")
    shown = 0
    for d, S in conf_deps:
        if not set(S) <= set(unit):
            continue
        locs = " or ".join(f"r{c//9}c{c%9}" for c in S)
        print(f"    digit {d} must be placed at {locs}")
        shown += 1
    if shown == 0:
        print("    (none at this confinement threshold)")

    res = SI.instances_for_stage(mask, max_confine=args.max_confine,
                                 max_instances=args.max_instances, seed=0)

    print(f"\ngeneration: {res['n_instances']} instances kept, "
          f"{res['n_rejected']} ruled out over {res['n_attempts']} attempts")
    print(f"coverage: {res['n_covered']}/{res['n_pairs']} "
          f"(cell, candidate) pairs = {100*res['coverage']:.1f}%")

    inst = res["instances"]
    if len(inst):
        print(f"\nwhat each instance assigned in {NAMES[args.unit]}:")
        hdr = "    inst  " + "  ".join(f"r{c//9}c{c%9}" for c in unit)
        print(hdr)
        for i, a in enumerate(inst):
            print(f"    {i:>4}  " + "  ".join(f"{a[c]:>4}" for c in unit))

        print(f"\nper-cell coverage in {NAMES[args.unit]}:")
        for c in unit:
            ds = set(SI.digits_of(mask[c]))
            seen = set(int(a[c]) for a in inst)
            miss = sorted(ds - seen)
            r, cc = divmod(c, 9)
            status = "complete" if not miss else f"missing {fmt(miss)}"
            print(f"    r{r}c{cc}  candidates {fmt(sorted(ds)):<22} "
                  f"seen {fmt(sorted(seen)):<22} {status}")


if __name__ == "__main__":
    main()