Sudoku_superposition / code /verify_superposition_instances.py
Avra98's picture
Add README and training/generation code
bb23b91 verified
Raw
History Blame Contribute Delete
3.59 kB
"""Statistics for superposition-instance generation across all stages.
Reports, per stage: how many instances are produced per puzzle, how much of each
candidate set the instances cover, how many proposals get ruled out by the
dependencies, and how the dependency load changes as the grid resolves.
"""
import argparse
import collections
import multiprocessing as mp
import numpy as np
import superposition_instances as SI
_MASKS = None
_ARGS = None
def _init(path, args):
global _MASKS, _ARGS
_MASKS = np.load(path, mmap_mode="r")
_ARGS = args
def _one(idx):
out = []
n_stages = _MASKS.shape[1]
for s in range(n_stages):
mask = np.array(_MASKS[idx, s]).astype(np.uint16)
r = SI.instances_for_stage(
mask, max_confine=_ARGS.max_confine,
max_instances=_ARGS.max_instances,
max_attempts=_ARGS.max_attempts, seed=idx * 100 + s,
max_repair=_ARGS.max_repair)
all_deps = SI.dependencies_from_mask(mask, max_confine=9)
r["n_deps_all"] = len(all_deps)
r.pop("instances")
r["stage"] = s
out.append(r)
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--masks", default="datasets_multicandidate_s12/test_cand_masks.npy")
ap.add_argument("--limit", type=int, default=200)
ap.add_argument("--max-confine", type=int, default=3)
ap.add_argument("--max-instances", type=int, default=32)
ap.add_argument("--max-attempts", type=int, default=200)
ap.add_argument("--max-repair", type=int, default=120)
ap.add_argument("--workers", type=int, default=32)
args = ap.parse_args()
masks = np.load(args.masks, mmap_mode="r")
n = min(args.limit, len(masks))
print(f"masks {masks.shape} from {args.masks}")
print(f"puzzles {n}, confinement threshold |S| <= {args.max_confine}, "
f"instance cap {args.max_instances}, attempt cap {args.max_attempts}")
with mp.Pool(args.workers, initializer=_init,
initargs=(args.masks, args)) as pool:
results = pool.map(_one, range(n), chunksize=4)
by_stage = collections.defaultdict(list)
for rows in results:
for r in rows:
by_stage[r["stage"]].append(r)
print()
print("=" * 112)
print("PER-STAGE INSTANCE GENERATION")
print("=" * 112)
print(f"{'stage':>5} {'empty':>6} {'width':>6} {'deps all':>9} "
f"{'deps enf':>9} {'kept':>6} {'ruled out':>10} {'reject %':>9} "
f"{'coverage':>9} {'capped %':>9} {'dup/inst':>9}")
print("-" * 112)
for s in sorted(by_stage):
rs = by_stage[s]
g = lambda k: np.mean([r[k] for r in rs])
rej = np.sum([r["n_rejected"] for r in rs])
att = np.sum([r["n_attempts"] for r in rs])
print(f"{s:>5} {g('n_empty'):>6.1f} {g('mean_width'):>6.2f} "
f"{g('n_deps_all'):>9.1f} {g('n_deps'):>9.1f} "
f"{g('n_instances'):>6.1f} {g('n_rejected'):>10.1f} "
f"{100*rej/max(att,1):>8.1f}% {100*g('coverage'):>8.1f}% "
f"{100*np.mean([r['hit_cap'] for r in rs]):>8.1f}% "
f"{g('mean_dups'):>9.2f}")
tot_inst = np.sum([r["n_instances"] for rs in by_stage.values() for r in rs])
tot_rej = np.sum([r["n_rejected"] for rs in by_stage.values() for r in rs])
print()
print(f"per puzzle across all {len(by_stage)} stages: "
f"{tot_inst/n:.1f} instances kept, {tot_rej/n:.1f} ruled out")
print(f"dataset multiplier vs one row per puzzle: {tot_inst/n:.1f}x")
if __name__ == "__main__":
main()