Skip to content

Commit 3b7a2e7

Browse files
committed
Add a few different scoring techniques
1 parent 03d442e commit 3b7a2e7

1 file changed

Lines changed: 70 additions & 48 deletions

File tree

codeclash/analysis/metrics/elo2.py

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from scipy.optimize import minimize
1111
from tqdm import tqdm
1212

13+
from codeclash.analysis.metrics.elo import get_scores
14+
from codeclash.analysis.significance import calculate_p_value
1315
from codeclash.constants import LOCAL_LOG_DIR, RESULT_TIE
1416
from codeclash.utils.log import get_logger
1517

@@ -21,12 +23,18 @@
2123

2224

2325
class ScoreMatrixBuilder:
24-
def __init__(self, *, all_normalization_scheme: Literal["none", "by_game_model_pair", "by_game"] = "none"):
26+
def __init__(
27+
self,
28+
*,
29+
all_normalization_scheme: Literal["none", "by_game_model_pair", "by_game"] = "none",
30+
round_score_type: Literal["tertiary", "float", "tertiary_p_value"] = "tertiary",
31+
):
2532
self.win_matrix: dict[str, dict[tuple[str, str], list[float]]] = defaultdict(
2633
lambda: defaultdict(lambda: [0.0, 0.0])
2734
)
28-
self.all_normalization_scheme = all_normalization_scheme
2935
"""game name -> (player1, player2) -> [wins, losses]"""
36+
self.all_normalization_scheme = all_normalization_scheme
37+
self.round_score_type = round_score_type
3038

3139
def _get_unique_model_name(self, model: str) -> str:
3240
return model.rpartition("/")[2]
@@ -39,52 +47,58 @@ def _get_score(self, stats: dict, player_names: list[str], game_name: str) -> tu
3947
4048
Returns (p1_score, p2_score) where each is 0.0, 0.5, or 1.0.
4149
"""
42-
if stats["winner"] == RESULT_TIE:
50+
if self.round_score_type == "float":
51+
scores = get_scores(stats)
52+
if len(stats["scores"]) == 1 and stats["scores"][RESULT_TIE] > 0:
53+
return (0.5, 0.5)
54+
# print(stats)
55+
return (scores[player_names[0]], scores[player_names[1]])
56+
elif self.round_score_type == "tertiary":
57+
if stats["winner"] == RESULT_TIE:
58+
return (0.5, 0.5)
59+
if stats["winner"] == player_names[0]:
60+
return (1.0, 0.0)
61+
elif stats["winner"] == player_names[1]:
62+
return (0.0, 1.0)
63+
raise ValueError(f"Expected winner to be one of {player_names}, got {stats['winner']}")
64+
elif self.round_score_type == "tertiary_p_value":
65+
player2score = stats["scores"]
66+
assert len(player_names) == 2
67+
68+
# Handle special case that one or more had an invalid submit
69+
valid_submits = sum(
70+
[x["valid_submit"] for x in stats["player_stats"].values() if x.get("valid_submit") is not None]
71+
)
72+
if valid_submits == 0:
73+
return (0.5, 0.5)
74+
if valid_submits == 1:
75+
if stats["winner"] == "Tie":
76+
return (0.5, 0.5)
77+
if stats["winner"] == player_names[0]:
78+
return (1, 0)
79+
else:
80+
return (0, 1)
81+
82+
# if len(player2score) != 2:
83+
# raise ValueError(f"Expected 2 players, got {len(player2score)}: {player2score}")
84+
85+
p1_name, p2_name = player_names
86+
if p1_name not in player2score or p2_name not in player2score:
87+
raise ValueError(f"Expected {p1_name} and {p2_name} in {player2score}")
88+
89+
# For HuskyBench and RoboCode, don't use significance testing
90+
if game_name not in ["HuskyBench", "RoboCode"]:
91+
p_value = calculate_p_value(player2score)
92+
if p_value > 0.05:
93+
return (0.5, 0.5)
94+
95+
# Determine winner
96+
if player2score[p1_name] > player2score[p2_name]:
97+
return (1.0, 0.0)
98+
elif player2score[p2_name] > player2score[p1_name]:
99+
return (0.0, 1.0)
43100
return (0.5, 0.5)
44-
if stats["winner"] == player_names[0]:
45-
return (1.0, 0.0)
46-
elif stats["winner"] == player_names[1]:
47-
return (0.0, 1.0)
48-
raise ValueError(f"Expected winner to be one of {player_names}, got {stats['winner']}")
49-
# player2score = stats["scores"]
50-
# assert len(player_names) == 2
51-
52-
# Handle special case that one or more had an invalid submit
53-
# valid_submits = sum(
54-
# [x["valid_submit"] for x in stats["player_stats"].values() if x.get("valid_submit") is not None]
55-
# )
56-
# if valid_submits == 2:
57-
# return (0, 0)
58-
# if valid_submits == 1:
59-
# if stats["winner"] == "Tie":
60-
# return (0, 0)
61-
# if stats["winner"] == player_names[0]:
62-
# return (1, 0)
63-
# else:
64-
# return (0, 1)
65-
66-
# print(stats)
67-
# print(player2score)
68-
69-
# if len(player2score) != 2:
70-
# raise ValueError(f"Expected 2 players, got {len(player2score)}: {player2score}")
71-
72-
# p1_name, p2_name = player_names
73-
# if p1_name not in player2score or p2_name not in player2score:
74-
# raise ValueError(f"Expected {p1_name} and {p2_name} in {player2score}")
75-
76-
# # For HuskyBench and RoboCode, don't use significance testing
77-
# if game_name not in ["HuskyBench", "RoboCode"]:
78-
# p_value = calculate_p_value(player2score)
79-
# if p_value > 0.05:
80-
# return (0, 0)
81-
82-
# # Determine winner
83-
# if player2score[p1_name] > player2score[p2_name]:
84-
# return (1, 0)
85-
# elif player2score[p2_name] > player2score[p1_name]:
86-
# return (0, 1)
87-
# raise ValueError(f"Shouldn't get here, p value should have been > 0.05: {player2score}, {p_value}")
101+
raise ValueError(f"Invalid round score type: {self.round_score_type}")
88102

89103
def _process_tournament(self, metadata_path: Path) -> None:
90104
metadata = json.loads(metadata_path.read_text())
@@ -435,6 +449,12 @@ def print_results(self, *, all_normalization_scheme: str = "none") -> None:
435449
parser = argparse.ArgumentParser(description="Build win matrix and fit Bradley-Terry model")
436450
parser.add_argument("-d", "--log_dir", type=Path, default=LOCAL_LOG_DIR)
437451
parser.add_argument("--print-matrix", action="store_true", help="Print win matrix")
452+
parser.add_argument(
453+
"--round-score-type",
454+
choices=["tertiary", "float", "tertiary_p_value"],
455+
default="tertiary",
456+
help="Round score type: 'tertiary' (0.0, 0.5, 1.0) or 'float' (0.0-1.0)",
457+
)
438458
parser.add_argument(
439459
"-l",
440460
"--lambda",
@@ -468,7 +488,9 @@ def print_results(self, *, all_normalization_scheme: str = "none") -> None:
468488
)
469489
args = parser.parse_args()
470490

471-
builder = ScoreMatrixBuilder(all_normalization_scheme=args.all_normalization_scheme)
491+
builder = ScoreMatrixBuilder(
492+
all_normalization_scheme=args.all_normalization_scheme, round_score_type=args.round_score_type
493+
)
472494
builder.build(args.log_dir)
473495

474496
if args.print_matrix:

0 commit comments

Comments
 (0)