summaryrefslogtreecommitdiff
path: root/src/eval_tune/compare_ecm_nodes.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval_tune/compare_ecm_nodes.py')
-rw-r--r--src/eval_tune/compare_ecm_nodes.py79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/eval_tune/compare_ecm_nodes.py b/src/eval_tune/compare_ecm_nodes.py
new file mode 100644
index 0000000..73408ef
--- /dev/null
+++ b/src/eval_tune/compare_ecm_nodes.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+"""Pair up per-problem node counts from two ECM sd=N stdout logs (same
+input file, same order) and report a median/geomean ratio plus win/loss
+counts -- robust to the handful of huge forced-mate outliers that would
+otherwise dominate a raw sum. Mate-hunt problems (last iterated depth
+line containing MATE) are reported separately since their node count is
+chaotic w.r.t. move-ordering and not representative of typical
+branching-factor efficiency.
+"""
+import re
+import statistics
+import sys
+
+SEARCHED_RE = re.compile(r"Searched for\s+[\d.]+ seconds, saw (\d+) nodes")
+DEPTH_LINE_RE = re.compile(r"^\s*\d+[+]?\s+(\S+)\s+[\d:.]+\s+(\d+)\s")
+
+
+def parse(path):
+ """Return list of (node_count, is_mate) per completed problem, in order."""
+ results = []
+ last_score = None
+ with open(path) as f:
+ for line in f:
+ m = DEPTH_LINE_RE.match(line)
+ if m:
+ last_score = m.group(1)
+ continue
+ m = SEARCHED_RE.search(line)
+ if m:
+ is_mate = bool(last_score and "MATE" in last_score)
+ results.append((int(m.group(1)), is_mate))
+ last_score = None
+ return results
+
+
+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))
+ base, cand = base[:n], cand[:n]
+
+ normal_ratios = []
+ mate_ratios = []
+ wins = losses = ties = 0
+ mate_wins = mate_losses = mate_ties = 0
+
+ for (bn, bmate), (cn, cmate) in zip(base, cand):
+ ratio = cn / bn if bn else 1.0
+ is_mate = bmate or cmate
+ bucket = mate_ratios if is_mate else normal_ratios
+ bucket.append(ratio)
+ if is_mate:
+ if cn < bn: mate_wins += 1
+ elif cn > bn: mate_losses += 1
+ else: mate_ties += 1
+ else:
+ if cn < bn: wins += 1
+ elif cn > bn: losses += 1
+ else: ties += 1
+
+ print(f"Paired problems: {n}")
+ print()
+ print(f"=== Non-mate problems (n={len(normal_ratios)}) ===")
+ if normal_ratios:
+ print(f" median candidate/baseline node ratio: {statistics.median(normal_ratios):.4f}")
+ print(f" geomean ratio: {statistics.geometric_mean(normal_ratios):.4f}")
+ print(f" wins (candidate fewer nodes): {wins}")
+ print(f" losses (candidate more nodes): {losses}")
+ print(f" ties: {ties}")
+ print()
+ print(f"=== Mate-hunt problems (n={len(mate_ratios)}) -- reported separately, chaotic ===")
+ if mate_ratios:
+ print(f" median ratio: {statistics.median(mate_ratios):.4f}")
+ print(f" wins: {mate_wins} losses: {mate_losses} ties: {mate_ties}")
+
+
+if __name__ == "__main__":
+ main()