@@ -28,9 +28,19 @@ def __call__(self, factor, structural: bool = False, grids: list = None,
2828
2929
3030class CustomEvaluator (EvaluatorTemplate ):
31- def __init__ (self , evaluation_functions_np : Union [Callable , dict ] = None ,
31+ def __init__ (self , evaluation_functions_np : Union [Callable , dict ] = None ,
3232 evaluation_functions_torch : Union [Callable , dict ] = None ,
33- eval_fun_params_labels : Union [list , tuple , set ] = ['power' ]):
33+ eval_fun_params_labels : Union [list , tuple , set ] = ['power' ],
34+ native_vectorized : bool = False ):
35+ """Wrap one or many evaluation functions for use as a factor evaluator.
36+
37+ ``native_vectorized=True`` skips the per-element ``np.vectorize``
38+ dispatch on the hot path: the func is called ONCE with the full
39+ grid arrays. The built-in evaluators in this module all set this
40+ flag because their numpy ops (``np.cos``, ``np.sin``, ``np.power``,
41+ ``np.full_like``, etc.) vectorize natively. User code passing a
42+ non-vectorising callable should leave the default ``False``.
43+ """
3444 self ._evaluation_functions_np = evaluation_functions_np
3545 self ._evaluation_functions_torch = evaluation_functions_torch
3646
@@ -43,6 +53,7 @@ def __init__(self, evaluation_functions_np: Union[Callable, dict] = None,
4353 self ._single_function_token = True
4454
4555 self .eval_fun_params_labels = eval_fun_params_labels
56+ self .native_vectorized = native_vectorized
4657
4758 def __call__ (self , factor , structural : bool = False , func_args : List [Union [torch .Tensor , np .ndarray ]] = None ,
4859 torch_mode : bool = False , ** kwargs ): # s
@@ -67,23 +78,32 @@ def __call__(self, factor, structural: bool = False, func_args: List[Union[torch
6778 if param_descr ['name' ] == key :
6879 eval_fun_kwargs [key ] = factor .params [param_idx ]
6980
70- grid_function = np .vectorize (lambda args : funcs (* args , ** eval_fun_kwargs ))
71-
7281 if func_args is None :
7382 new_grid = False
7483 func_args = factor .grids
7584 else :
7685 new_grid = True
77- try :
78- if new_grid :
79- raise AttributeError
80- self .indexes_vect
81- except AttributeError :
82- self .indexes_vect = np .empty_like (func_args [0 ], dtype = object )
83- for tensor_idx , _ in np .ndenumerate (func_args [0 ]):
84- self .indexes_vect [tensor_idx ] = tuple ([subarg [tensor_idx ]
85- for subarg in func_args ])
86- value = grid_function (self .indexes_vect )
86+
87+ if self .native_vectorized :
88+ # Fast path: call funcs once with the full grid arrays. The
89+ # built-in numpy evaluators (trig, sign, grid, inverse,
90+ # const, velocity) all return an array of shape
91+ # ``func_args[0].shape``. This skips an N-element
92+ # ``np.vectorize`` loop that on Wave (65k samples)
93+ # dominated evaluator self-time at ~35 s per run.
94+ value = funcs (* func_args , ** eval_fun_kwargs )
95+ else :
96+ grid_function = np .vectorize (lambda args : funcs (* args , ** eval_fun_kwargs ))
97+ try :
98+ if new_grid :
99+ raise AttributeError
100+ self .indexes_vect
101+ except AttributeError :
102+ self .indexes_vect = np .empty_like (func_args [0 ], dtype = object )
103+ for tensor_idx , _ in np .ndenumerate (func_args [0 ]):
104+ self .indexes_vect [tensor_idx ] = tuple ([subarg [tensor_idx ]
105+ for subarg in func_args ])
106+ value = grid_function (self .indexes_vect )
87107 value = value [global_var .grid_cache .g_func != 0 ]
88108 value = value .reshape (- 1 )
89109 return value
@@ -125,7 +145,9 @@ def simple_function_evaluator(factor, structural: bool = False, grids=None,
125145
126146 else :
127147 if factor .params [power_param_idx ] == 1 :
128- value = global_var .tensor_cache .get (factor .cache_label , structural = structural , torch_mode = torch_mode )
148+ # Same bucketed key Factor.evaluate uses so trig factors with
149+ # within-tolerance freq share a single cached evaluation.
150+ value = global_var .tensor_cache .get (factor .structural_label , structural = structural , torch_mode = torch_mode )
129151 return value
130152 else :
131153 value = global_var .tensor_cache .get (factor_params_to_str (factor , set_default_power = True ,
@@ -259,30 +281,37 @@ def vhef_grad_15(*grids, **kwargs):
259281 vhef_grad_10 , vhef_grad_11 , vhef_grad_12 ,
260282 vhef_grad_13 , vhef_grad_14 , vhef_grad_15 ]
261283
262- sign_evaluator = CustomEvaluator (evaluation_functions_np = sign_eval_fun_np ,
263- evaluation_functions_torch = sign_eval_fun_torch ,
264- eval_fun_params_labels = ['power' , 'dim' ])
284+ sign_evaluator = CustomEvaluator (evaluation_functions_np = sign_eval_fun_np ,
285+ evaluation_functions_torch = sign_eval_fun_torch ,
286+ eval_fun_params_labels = ['power' , 'dim' ],
287+ native_vectorized = True )
265288
266- phased_sine_evaluator = CustomEvaluator (evaluation_functions_np = phased_sine_1d_np ,
289+ phased_sine_evaluator = CustomEvaluator (evaluation_functions_np = phased_sine_1d_np ,
267290 evaluation_functions_torch = phased_sine_1d_torch ,
268- eval_fun_params_labels = ['power' , 'freq' , 'phase' ]) # , use_factors_grids = True
291+ eval_fun_params_labels = ['power' , 'freq' , 'phase' ],
292+ native_vectorized = True ) # , use_factors_grids = True
269293trigonometric_evaluator = CustomEvaluator (evaluation_functions_np = trig_eval_fun_np ,
270294 evaluation_functions_torch = trig_eval_fun_torch ,
271- eval_fun_params_labels = ['freq' , 'dim' , 'power' ]) # , use_factors_grids = True
295+ eval_fun_params_labels = ['freq' , 'dim' , 'power' ],
296+ native_vectorized = True ) # , use_factors_grids = True
272297grid_evaluator = CustomEvaluator (evaluation_functions_np = grid_eval_fun_np ,
273298 evaluation_functions_torch = grid_eval_fun_torch ,
274- eval_fun_params_labels = ['dim' , 'power' ]) # , use_factors_grids=True
299+ eval_fun_params_labels = ['dim' , 'power' ],
300+ native_vectorized = True ) # , use_factors_grids=True
275301
276302inverse_function_evaluator = CustomEvaluator (evaluation_functions_np = inverse_eval_fun_np ,
277303 evaluation_functions_torch = inverse_eval_fun_torch ,
278- eval_fun_params_labels = ['dim' , 'power' ]) # , use_factors_grids=True
304+ eval_fun_params_labels = ['dim' , 'power' ],
305+ native_vectorized = True ) # , use_factors_grids=True
279306
280307const_evaluator = CustomEvaluator (evaluation_functions_np = const_eval_fun_np ,
281- evaluation_functions_torch = const_eval_fun_torch ,
282- eval_fun_params_labels = ['power' , 'value' ])
308+ evaluation_functions_torch = const_eval_fun_torch ,
309+ eval_fun_params_labels = ['power' , 'value' ],
310+ native_vectorized = True )
283311const_grad_evaluator = CustomEvaluator (evaluation_functions_np = const_grad_fun_np ,
284312 evaluation_functions_torch = const_grad_fun_np ,
285- eval_fun_params_labels = ['power' , 'value' ])
313+ eval_fun_params_labels = ['power' , 'value' ],
314+ native_vectorized = True )
286315
287316velocity_evaluator = CustomEvaluator (velocity_heating_eval_fun , ['p' + str (idx + 1 ) for idx in range (15 )])
288317velocity_grad_evaluators = [CustomEvaluator (component , ['p' + str (idx + 1 ) for idx in range (15 )])
0 commit comments