Sudoku_superposition / code /show_superposition_example.py
Avra98's picture
Add README and training/generation code
bb23b91 verified
Raw
History Blame Contribute Delete
4.02 kB
"""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()