@@ -61,15 +61,26 @@ def evaluate_batch(self, coeffs: Any, batch: QualifiedBatch) -> np.ndarray:
6161class LinearApproximator (Approximator ):
6262 """Piecewise multilinear interpolation on CartesianGrid."""
6363
64- def __init__ (self , grid : CartesianGrid , space : Optional [CartesianSpace ] = None ):
64+ def __init__ (
65+ self ,
66+ grid : CartesianGrid ,
67+ space : Optional [CartesianSpace ] = None ,
68+ backend : str = "jax" ,
69+ ):
6570 self .grid = grid
6671 self .space = space
72+ self .backend = backend
6773
6874 def fit (self , values : Union [GArray , np .ndarray ]) -> np .ndarray :
6975 data = values .data if isinstance (values , GArray ) else np .asarray (values )
7076 return data .reshape (tuple (self .grid .n ) + (- 1 ,)).copy ()
7177
7278 def evaluate_coords (self , coeffs : np .ndarray , coords : np .ndarray ) -> np .ndarray :
79+ if self .backend == "jax" :
80+ from dolo .numeric .interpolation_jax import eval_linear_jax
81+
82+ return np .asarray (eval_linear_jax (self .grid , coeffs , coords ))
83+
7384 from interpolation .splines import eval_linear
7485
7586 pts = np .asarray (coords , dtype = float )
@@ -81,18 +92,34 @@ def evaluate_coords(self, coeffs: np.ndarray, coords: np.ndarray) -> np.ndarray:
8192class CubicSplineApproximator (Approximator ):
8293 """Prefiltered cubic spline interpolation on CartesianGrid."""
8394
84- def __init__ (self , grid : CartesianGrid , space : Optional [CartesianSpace ] = None ):
95+ def __init__ (
96+ self ,
97+ grid : CartesianGrid ,
98+ space : Optional [CartesianSpace ] = None ,
99+ backend : str = "jax" ,
100+ ):
85101 self .grid = grid
86102 self .space = space
103+ self .backend = backend
87104
88105 def fit (self , values : Union [GArray , np .ndarray ]) -> np .ndarray :
89- from interpolation .splines .prefilter_cubic import prefilter_cubic
90-
91106 data = values .data if isinstance (values , GArray ) else np .asarray (values )
92107 reshaped = data .reshape (tuple (self .grid .n ) + (- 1 ,))
108+ if self .backend == "jax" :
109+ from dolo .numeric .interpolation_jax import prefilter_cubic_jax
110+
111+ return np .asarray (prefilter_cubic_jax (self .grid , reshaped ))
112+
113+ from interpolation .splines .prefilter_cubic import prefilter_cubic
114+
93115 return prefilter_cubic (self .grid .__numba_repr__ (), reshaped )
94116
95117 def evaluate_coords (self , coeffs : np .ndarray , coords : np .ndarray ) -> np .ndarray :
118+ if self .backend == "jax" :
119+ from dolo .numeric .interpolation_jax import eval_cubic_jax
120+
121+ return np .asarray (eval_cubic_jax (self .grid , coeffs , coords ))
122+
96123 from interpolation .splines import eval_cubic
97124
98125 pts = np .asarray (coords , dtype = float )
@@ -172,6 +199,8 @@ def __init__(
172199 def fit (self , values : Union [GArray , np .ndarray ]) -> Any :
173200 data = values .data if isinstance (values , GArray ) else np .asarray (values )
174201 if self .is_empty_exo :
202+ if data .ndim == 3 and data .shape [0 ] == 1 :
203+ data = data [0 ]
175204 return self .sub_approximator .fit (data )
176205 elif self .is_discrete_exo :
177206 n_regimes = data .shape [0 ]
@@ -183,6 +212,48 @@ def fit(self, values: Union[GArray, np.ndarray]) -> Any:
183212 f"Unsupported exo grid type: { type (self .exo_grid )} "
184213 )
185214
215+ def evaluate_s (self , coeffs : Any , s : np .ndarray ) -> np .ndarray :
216+ """Evaluate continuous endogenous states `s` when exogenous state is empty or single-regime."""
217+ s_arr = np .asarray (s , dtype = float )
218+ if self .is_empty_exo :
219+ return self .sub_approximator .evaluate_coords (coeffs , s_arr )
220+ if self .is_discrete_exo and self .exo_grid .n_nodes == 1 :
221+ return self .sub_approximator .evaluate_coords (coeffs [0 ], s_arr )
222+ raise ValueError (
223+ "Cannot evaluate 's' alone when exogenous states are non-empty."
224+ )
225+
226+ def evaluate_ms (self , coeffs : Any , m : np .ndarray , s : np .ndarray ) -> np .ndarray :
227+ """Evaluate continuous endogenous states `s` and exogenous states `m`."""
228+ m_arr = np .asarray (m , dtype = float )
229+ s_arr = np .asarray (s , dtype = float )
230+ if self .is_empty_exo :
231+ return self .sub_approximator .evaluate_coords (coeffs , s_arr )
232+ elif self .is_cartesian_exo :
233+ combined = np .hstack ([m_arr , s_arr ])
234+ return self .joint_approximator .evaluate_coords (coeffs , combined )
235+ elif self .is_discrete_exo :
236+ if m_arr .ndim == 1 :
237+ m_arr = m_arr [None , :]
238+ if s_arr .ndim == 1 :
239+ s_arr = s_arr [None , :]
240+ if m_arr .shape [0 ] == 1 and s_arr .shape [0 ] > 1 :
241+ m_arr = np .repeat (m_arr , s_arr .shape [0 ], axis = 0 )
242+ diffs = np .linalg .norm (
243+ self .exo_grid .nodes [:, None , :] - m_arr [None , :, :], axis = - 1
244+ )
245+ regimes = np .argmin (diffs , axis = 0 )
246+ n_x = coeffs [0 ].shape [- 1 ] if hasattr (coeffs [0 ], "shape" ) else 1
247+ out = np .empty ((s_arr .shape [0 ], n_x ), dtype = float )
248+ for r in np .unique (regimes ):
249+ mask = regimes == r
250+ out [mask ] = self .sub_approximator .evaluate_coords (
251+ coeffs [r ], s_arr [mask ]
252+ )
253+ return out
254+ else :
255+ raise NotImplementedError ()
256+
186257 def evaluate_coords (self , coeffs : Any , coords : np .ndarray ) -> np .ndarray :
187258 if self .is_empty_exo :
188259 return self .sub_approximator .evaluate_coords (coeffs , coords )
0 commit comments