Spaces:
Sleeping
Sleeping
| """Aggregate engine vs move-counterfactual heuristic vectors across games.""" | |
| from __future__ import annotations | |
| from typing import Any, Dict, List, Sequence | |
| import numpy as np | |
| from chess_tutor.analysis.types import MoveHeuristicVectors | |
| def mean_abs_heuristic_delta(vectors: MoveHeuristicVectors) -> float: | |
| """Mean |move_heuristic − engine_heuristic| over MLP outputs (0–100 units).""" | |
| ref = np.asarray(vectors.reference_0_100, dtype=np.float64) | |
| inv = np.asarray(vectors.inverted_0_100, dtype=np.float64) | |
| return float(np.mean(np.abs(inv - ref))) | |
| def mean_abs_heuristic_delta_chained(chained: Any) -> float: | |
| """Adapter for ``ChainedMoveConceptResult`` (lazy import avoids cycles in tests).""" | |
| from chess_tutor.analysis.adapters import vectors_from_chained | |
| return mean_abs_heuristic_delta(vectors_from_chained(chained)) | |
| def vec_dict_add(store: Dict[str, np.ndarray], key: str, vec: np.ndarray) -> None: | |
| v = np.asarray(vec, dtype=np.float64).reshape(-1) | |
| if key not in store: | |
| store[key] = np.zeros_like(v) | |
| store[key] = store[key] + v | |
| def per_heuristic_breakdown_rows( | |
| names: Sequence[str], | |
| n_moves: int, | |
| ref_sum: np.ndarray, | |
| inv_sum: np.ndarray, | |
| abs_diff_sum: np.ndarray, | |
| signed_diff_sum: np.ndarray, | |
| ) -> List[Dict[str, Any]]: | |
| """One row per heuristic dimension for a game or for all moves pooled.""" | |
| if n_moves <= 0: | |
| return [] | |
| n_moves_f = float(n_moves) | |
| ref_m = ref_sum / n_moves_f | |
| inv_m = inv_sum / n_moves_f | |
| mean_abs = abs_diff_sum / n_moves_f | |
| mean_signed = signed_diff_sum / n_moves_f | |
| out: List[Dict[str, Any]] = [] | |
| for i, name in enumerate(names): | |
| out.append( | |
| { | |
| "heuristic": str(name), | |
| "engine_mean_0_100": round(float(ref_m[i]), 4), | |
| "move_counterfactual_mean_0_100": round(float(inv_m[i]), 4), | |
| "mean_signed_difference_move_minus_engine": round(float(mean_signed[i]), 4), | |
| "mean_abs_difference_across_moves": round(float(mean_abs[i]), 4), | |
| } | |
| ) | |
| return out | |
| def per_heuristic_equal_weight_across_games( | |
| names: Sequence[str], | |
| per_game_ref_sum: Dict[str, np.ndarray], | |
| per_game_inv_sum: Dict[str, np.ndarray], | |
| per_game_abs_sum: Dict[str, np.ndarray], | |
| per_game_signed_sum: Dict[str, np.ndarray], | |
| per_game_succ: Dict[str, int], | |
| ) -> List[Dict[str, Any]]: | |
| """Average per-game means so each game counts equally.""" | |
| games = [gk for gk, n in per_game_succ.items() if n > 0 and gk in per_game_ref_sum] | |
| if not games: | |
| return [] | |
| n_dim = len(names) | |
| rows: List[Dict[str, Any]] = [] | |
| for i in range(n_dim): | |
| eng_g = [per_game_ref_sum[gk][i] / float(per_game_succ[gk]) for gk in games] | |
| mv_g = [per_game_inv_sum[gk][i] / float(per_game_succ[gk]) for gk in games] | |
| abs_g = [per_game_abs_sum[gk][i] / float(per_game_succ[gk]) for gk in games] | |
| sg_g = [per_game_signed_sum[gk][i] / float(per_game_succ[gk]) for gk in games] | |
| eng_m = float(np.mean(np.asarray(eng_g, dtype=np.float64))) | |
| mv_m = float(np.mean(np.asarray(mv_g, dtype=np.float64))) | |
| rows.append( | |
| { | |
| "heuristic": str(names[i]), | |
| "engine_mean_across_games_equal_weight": round(eng_m, 4), | |
| "move_counterfactual_mean_across_games_equal_weight": round(mv_m, 4), | |
| "mean_signed_difference_move_minus_engine": round(mv_m - eng_m, 4), | |
| "mean_abs_difference_averaged_per_game_then_across_games": round( | |
| float(np.mean(np.asarray(abs_g, dtype=np.float64))), 4 | |
| ), | |
| "mean_signed_difference_averaged_per_game_then_across_games": round( | |
| float(np.mean(np.asarray(sg_g, dtype=np.float64))), 4 | |
| ), | |
| } | |
| ) | |
| return rows | |
| def summary_across_heuristics(per_heuristic_rows: Sequence[Dict[str, Any]]) -> Dict[str, Any]: | |
| """Mean of per-dimension scalars (twelve values → one summary number).""" | |
| if not per_heuristic_rows: | |
| return { | |
| "mean_abs_difference_averaged_over_heuristics": None, | |
| "mean_signed_difference_averaged_over_heuristics": None, | |
| } | |
| abs_v = np.asarray( | |
| [r["mean_abs_difference_across_moves"] for r in per_heuristic_rows], dtype=np.float64 | |
| ) | |
| sg_v = np.asarray( | |
| [r["mean_signed_difference_move_minus_engine"] for r in per_heuristic_rows], | |
| dtype=np.float64, | |
| ) | |
| return { | |
| "mean_abs_difference_averaged_over_heuristics": round(float(np.mean(abs_v)), 4), | |
| "mean_signed_difference_averaged_over_heuristics": round(float(np.mean(sg_v)), 4), | |
| } | |
| def summary_across_heuristics_equal_game( | |
| rows: Sequence[Dict[str, Any]], | |
| ) -> Dict[str, Any]: | |
| if not rows: | |
| return { | |
| "mean_abs_difference_averaged_over_heuristics": None, | |
| "mean_signed_difference_averaged_over_heuristics": None, | |
| } | |
| abs_v = np.asarray( | |
| [r["mean_abs_difference_averaged_per_game_then_across_games"] for r in rows], | |
| dtype=np.float64, | |
| ) | |
| sg_v = np.asarray( | |
| [r["mean_signed_difference_move_minus_engine"] for r in rows], | |
| dtype=np.float64, | |
| ) | |
| return { | |
| "mean_abs_difference_averaged_over_heuristics": round(float(np.mean(abs_v)), 4), | |
| "mean_signed_difference_averaged_over_heuristics": round(float(np.mean(sg_v)), 4), | |
| } | |