1010from scipy .optimize import minimize
1111from tqdm import tqdm
1212
13+ from codeclash .analysis .metrics .elo import get_scores
14+ from codeclash .analysis .significance import calculate_p_value
1315from codeclash .constants import LOCAL_LOG_DIR , RESULT_TIE
1416from codeclash .utils .log import get_logger
1517
2123
2224
2325class 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