| """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() |
|
|