#!/usr/bin/env python3 """Pair up per-problem node counts at the DEEPEST depth BOTH binaries completed within their sn=N node budget (not the final/achieved depth, which is too coarse -- see compare_ecm_depth.py). Continuous signal, still fully bounded since we only ever compare depths both sides actually finished. """ import re import statistics import sys DEPTH_LINE_RE = re.compile(r"^\s*(\d+)[+]?\s+(\S+)\s+[\d:.]+\s+(\d+)\s") ROOT_POS_RE = re.compile(r"^The root position is:") def parse(path): """Return list of {depth: node_count} dicts, one per problem, in order.""" problems = [] cur = {} with open(path) as f: for line in f: if ROOT_POS_RE.match(line): if cur: problems.append(cur) cur = {} continue m = DEPTH_LINE_RE.match(line) if m: depth = int(m.group(1)) nodes = int(m.group(3)) cur[depth] = nodes # later (deeper/re-searched) lines overwrite if cur: problems.append(cur) return problems def main(): base_path, cand_path = sys.argv[1], sys.argv[2] base = parse(base_path) cand = parse(cand_path) n = min(len(base), len(cand)) ratios = [] wins = losses = ties = 0 skipped = 0 for i in range(n): b, c = base[i], cand[i] shared_depths = set(b) & set(c) if not shared_depths: skipped += 1 continue d = max(shared_depths) bn, cn = b[d], c[d] if bn == 0: skipped += 1 continue ratio = cn / bn ratios.append(ratio) if cn < bn: wins += 1 elif cn > bn: losses += 1 else: ties += 1 print(f"Paired problems: {n} (usable: {len(ratios)}, skipped: {skipped})") if ratios: print(f" median candidate/baseline ratio at shared depth: {statistics.median(ratios):.4f}") print(f" geomean ratio: {statistics.geometric_mean(ratios):.4f}") print(f" wins (candidate fewer nodes): {wins}") print(f" losses (candidate more nodes): {losses}") print(f" ties: {ties}") if __name__ == "__main__": main()