@@ -22,46 +22,106 @@ def is_close(error: Error, tolerance: Tolerance) -> bool:
2222def compare (candidate : Pack , golden : Pack , tol : Tolerance ) -> typing .Tuple [Error , str ]:
2323 # Keep track of the average error
2424 avg_err = AverageError ()
25-
25+
2626 # Compare entry-count
2727 if len (candidate .entries ) != len (golden .entries ):
2828 return None , "Line count does not match."
29-
29+
3030 # For every entry in the golden's pack
3131 for gFilepath , gEntry in golden .entries .items ():
3232 # Find the corresponding entry in the candidate's pack
3333 cEntry = candidate .find (gFilepath )
34-
3534 if cEntry is None :
3635 return None , f"No reference to { gFilepath } in the candidate's pack."
37-
36+
3837 # Compare variable-count
3938 if len (gEntry .doubles ) != len (cEntry .doubles ):
4039 return None , f"Variable count didn't match for { gFilepath } ."
41-
40+
4241 # Check if each variable is within tolerance
4342 for valIndex , (gVal , cVal ) in enumerate (zip (gEntry .doubles , cEntry .doubles )):
4443 # Keep track of the error and average errors
4544 error = compute_error (cVal , gVal )
4645 avg_err .push (error )
47-
48- def raise_err (msg : str ):
46+
47+ def raise_err_with_max_diagnostics (msg : str ):
48+ # Find maximum errors across ALL files for diagnostics
49+ max_abs_info , max_rel_info = find_maximum_errors (candidate , golden )
50+
51+ diagnostic_msg = ""
52+ if max_abs_info :
53+ max_abs_filepath , max_abs_valIndex , max_abs_gVal , max_abs_cVal , max_abs_error , max_abs_rel_error = max_abs_info
54+ rel_error_str = f"{ max_abs_rel_error :.2E} " if not math .isnan (max_abs_rel_error ) else "NaN"
55+ diagnostic_msg += f"\n \n Diagnostics - Maximum absolute error across ALL files:\n " \
56+ f" - File: { max_abs_filepath } \n " \
57+ f" - Variable n°{ max_abs_valIndex + 1 } \n " \
58+ f" - Candidate: { max_abs_cVal } \n " \
59+ f" - Golden: { max_abs_gVal } \n " \
60+ f" - Absolute Error: { max_abs_error :.2E} \n " \
61+ f" - Relative Error: { rel_error_str } "
62+
63+ if max_rel_info :
64+ max_rel_filepath , max_rel_valIndex , max_rel_gVal , max_rel_cVal , max_rel_error , max_rel_abs_error = max_rel_info
65+ diagnostic_msg += f"\n \n Diagnostics - Maximum relative error across ALL files:\n " \
66+ f" - File: { max_rel_filepath } \n " \
67+ f" - Variable n°{ max_rel_valIndex + 1 } \n " \
68+ f" - Candidate: { max_rel_cVal } \n " \
69+ f" - Golden: { max_rel_gVal } \n " \
70+ f" - Relative Error: { max_rel_error :.2E} \n " \
71+ f" - Absolute Error: { max_rel_abs_error :.2E} "
72+
4973 return None , f"""\
5074 Variable n°{ valIndex + 1 } (1-indexed) in { gFilepath } { msg } :
51- - Candidate: { cVal }
52- - Golden: { gVal }
53- - Error: { error }
54- - Tolerance: { tol }
75+ - Candidate: { cVal }
76+ - Golden: { gVal }
77+ - Error: { error }
78+ - Tolerance: { tol } { diagnostic_msg }
5579"""
56-
80+
5781 if math .isnan (gVal ):
58- return raise_err ("is NaN in the golden file" )
59-
82+ return raise_err_with_max_diagnostics ("is NaN in the golden file" )
6083 if math .isnan (cVal ):
61- return raise_err ("is NaN in the pack file" )
62-
84+ return raise_err_with_max_diagnostics ("is NaN in the pack file" )
6385 if not is_close (error , tol ):
64- return raise_err ("is not within tolerance" )
65-
86+ return raise_err_with_max_diagnostics ("is not within tolerance" )
87+
6688 # Return the average relative error
6789 return avg_err .get (), None
90+
91+
92+ def find_maximum_errors (candidate : Pack , golden : Pack ) -> typing .Tuple [typing .Optional [typing .Tuple [str , int , float , float , float , float ]], typing .Optional [typing .Tuple [str , int , float , float , float , float ]]]:
93+ """
94+ Scan all files to find the maximum absolute and relative errors.
95+ Returns tuple of:
96+ - max_abs_info: (filepath, var_index, golden_val, candidate_val, absolute_error, relative_error)
97+ - max_rel_info: (filepath, var_index, golden_val, candidate_val, relative_error, absolute_error)
98+ """
99+ max_abs_error = - 1.0
100+ max_abs_info = None
101+
102+ max_rel_error = - 1.0
103+ max_rel_info = None
104+
105+ for gFilepath , gEntry in golden .entries .items ():
106+ cEntry = candidate .find (gFilepath )
107+ if cEntry is None :
108+ continue
109+
110+ for valIndex , (gVal , cVal ) in enumerate (zip (gEntry .doubles , cEntry .doubles )):
111+ # Skip NaN values in golden or candidate
112+ if math .isnan (gVal ) or math .isnan (cVal ):
113+ continue
114+
115+ error = compute_error (cVal , gVal )
116+
117+ # Track maximum absolute error
118+ if error .absolute > max_abs_error :
119+ max_abs_error = error .absolute
120+ max_abs_info = (gFilepath , valIndex , gVal , cVal , error .absolute , error .relative )
121+
122+ # Track maximum relative error (only if it's not NaN)
123+ if not math .isnan (error .relative ) and error .relative > max_rel_error :
124+ max_rel_error = error .relative
125+ max_rel_info = (gFilepath , valIndex , gVal , cVal , error .relative , error .absolute )
126+
127+ return max_abs_info , max_rel_info
0 commit comments