@@ -18,6 +18,34 @@ def is_close(error: Error, tolerance: Tolerance) -> bool:
1818 return False
1919
2020
21+ def _format_error_diagnostics (max_abs_info , max_rel_info ) -> str :
22+ """Format diagnostic information for maximum errors among failing variables."""
23+ diagnostic_msg = ""
24+
25+ if max_abs_info :
26+ filepath , val_idx , g_val , c_val , abs_err , rel_err = max_abs_info
27+ rel_error_str = f"{ rel_err :.2E} " if not math .isnan (rel_err ) else "NaN"
28+ diagnostic_msg += f"\n \n Diagnostics - Maximum absolute error among FAILING variables:\n " \
29+ f" - File: { filepath } \n " \
30+ f" - Variable n°{ val_idx + 1 } \n " \
31+ f" - Candidate: { c_val } \n " \
32+ f" - Golden: { g_val } \n " \
33+ f" - Absolute Error: { abs_err :.2E} \n " \
34+ f" - Relative Error: { rel_error_str } "
35+
36+ if max_rel_info :
37+ filepath , val_idx , g_val , c_val , rel_err , abs_err = max_rel_info
38+ diagnostic_msg += f"\n \n Diagnostics - Maximum relative error among FAILING variables:\n " \
39+ f" - File: { filepath } \n " \
40+ f" - Variable n°{ val_idx + 1 } \n " \
41+ f" - Candidate: { c_val } \n " \
42+ f" - Golden: { g_val } \n " \
43+ f" - Relative Error: { rel_err :.2E} \n " \
44+ f" - Absolute Error: { abs_err :.2E} "
45+
46+ return diagnostic_msg
47+
48+
2149# pylint: disable=too-many-return-statements
2250def compare (candidate : Pack , golden : Pack , tol : Tolerance ) -> typing .Tuple [Error , str ]:
2351 # Keep track of the average error
@@ -45,23 +73,69 @@ def compare(candidate: Pack, golden: Pack, tol: Tolerance) -> typing.Tuple[Error
4573 error = compute_error (cVal , gVal )
4674 avg_err .push (error )
4775
48- def raise_err (msg : str ):
76+ def raise_err_with_failing_diagnostics (msg : str ):
77+ # Find maximum errors among FAILING variables only
78+ max_abs_info , max_rel_info = find_maximum_errors_among_failing (candidate , golden , tol )
79+ diagnostic_msg = _format_error_diagnostics (max_abs_info , max_rel_info )
80+
4981 return None , f"""\
5082 Variable n°{ valIndex + 1 } (1-indexed) in { gFilepath } { msg } :
5183 - Candidate: { cVal }
5284 - Golden: { gVal }
5385 - Error: { error }
54- - Tolerance: { tol }
86+ - Tolerance: { tol } { diagnostic_msg }
5587"""
5688
5789 if math .isnan (gVal ):
58- return raise_err ("is NaN in the golden file" )
59-
90+ return raise_err_with_failing_diagnostics ("is NaN in the golden file" )
6091 if math .isnan (cVal ):
61- return raise_err ("is NaN in the pack file" )
62-
92+ return raise_err_with_failing_diagnostics ("is NaN in the pack file" )
6393 if not is_close (error , tol ):
64- return raise_err ("is not within tolerance" )
94+ return raise_err_with_failing_diagnostics ("is not within tolerance" )
6595
6696 # Return the average relative error
6797 return avg_err .get (), None
98+
99+
100+ def find_maximum_errors_among_failing (candidate : Pack , golden : Pack , tol : Tolerance ) -> typing .Tuple [typing .Optional [typing .Tuple [str , int , float , float , float , float ]], typing .Optional [typing .Tuple [str , int , float , float , float , float ]]]:
101+ """
102+ Scan all files to find the maximum absolute and relative errors among FAILING variables only.
103+ A variable fails if is_close(error, tol) returns False.
104+
105+ Returns tuple of:
106+ - max_abs_info: (filepath, var_index, golden_val, candidate_val, absolute_error, relative_error)
107+ - max_rel_info: (filepath, var_index, golden_val, candidate_val, relative_error, absolute_error)
108+ """
109+ max_abs_error = - 1.0
110+ max_abs_info = None
111+
112+ max_rel_error = - 1.0
113+ max_rel_info = None
114+
115+ for gFilepath , gEntry in golden .entries .items ():
116+ cEntry = candidate .find (gFilepath )
117+ if cEntry is None :
118+ continue
119+
120+ for valIndex , (gVal , cVal ) in enumerate (zip (gEntry .doubles , cEntry .doubles )):
121+ # Skip NaN values in golden or candidate
122+ if math .isnan (gVal ) or math .isnan (cVal ):
123+ continue
124+
125+ error = compute_error (cVal , gVal )
126+
127+ # Only consider variables that FAIL tolerance
128+ if is_close (error , tol ):
129+ continue # Skip variables that pass tolerance
130+
131+ # Track maximum absolute error among failing variables
132+ if error .absolute > max_abs_error :
133+ max_abs_error = error .absolute
134+ max_abs_info = (gFilepath , valIndex , gVal , cVal , error .absolute , error .relative )
135+
136+ # Track maximum relative error among failing variables (only if it's not NaN)
137+ if not math .isnan (error .relative ) and error .relative > max_rel_error :
138+ max_rel_error = error .relative
139+ max_rel_info = (gFilepath , valIndex , gVal , cVal , error .relative , error .absolute )
140+
141+ return max_abs_info , max_rel_info
0 commit comments