1616import gymnasium as gym
1717from gymnasium import spaces
1818
19- from das .env .observation import compute_observation , observation_dim
19+ from das .env .observation import compute_observation , observation_dim , MAX_HISTORY_SAMPLE
2020from das .env .reward import compute_reward
2121from das .optimizers .base import get_checkpoints
2222
@@ -251,14 +251,27 @@ def _update_episode_state(self, result: dict, prev_best_y: float):
251251 if worst_y > self ._worst_y :
252252 self ._worst_y = worst_y
253253
254- # Set initial range on first step
254+ # Set initial range on first step.
255+ # When worst_so_far_y is absent the default is -inf, which collapses
256+ # scale to 1e-5 and inflates every subsequent reward by 1e5. Instead,
257+ # derive scale from the magnitude of the initial best fitness.
255258 if self ._initial_range [0 ] == float ("inf" ):
256- self ._initial_range = (new_best_y , max (worst_y , new_best_y + 1e-5 ))
259+ safe_worst = (
260+ worst_y
261+ if np .isfinite (worst_y )
262+ else new_best_y + max (abs (new_best_y ), 1.0 )
263+ )
264+ self ._initial_range = (new_best_y , max (safe_worst , new_best_y + 1e-5 ))
257265
258- # Stagnation counter
266+ # Stagnation counter — prefer the FE delta from the result dict so that
267+ # stagnation accumulates correctly even when y_history is not returned.
259268 x_hist : np .ndarray | None = result .get ("x_history" )
260269 y_hist : np .ndarray | None = result .get ("y_history" )
261- n_fe_step = len (y_hist ) if y_hist is not None else 0
270+ n_fe_reported = result .get ("n_function_evaluations" )
271+ if n_fe_reported is not None :
272+ n_fe_step = max (0 , n_fe_reported - self ._n_fe )
273+ else :
274+ n_fe_step = len (y_hist ) if y_hist is not None else 0
262275
263276 if new_best_y >= prev_best_y :
264277 self ._stagnation_count += n_fe_step
@@ -267,20 +280,23 @@ def _update_episode_state(self, result: dict, prev_best_y: float):
267280
268281 self ._n_fe = result .get ("n_function_evaluations" , self ._n_fe + n_fe_step )
269282
270- # Accumulate population history for ELA
283+ # Accumulate population history for ELA, capped at MAX_HISTORY_SAMPLE rows.
284+ # Without the cap, large budgets (e.g. 40-dim × 10 000 FE) accumulate
285+ # hundreds of thousands of rows — GBs of RAM for a single episode.
271286 if x_hist is not None and len (x_hist ) > 0 :
272287 self ._x_history = (
273- x_hist
288+ x_hist [ - MAX_HISTORY_SAMPLE :]
274289 if self ._x_history is None
275- else np .concatenate ([self ._x_history , x_hist ])
290+ else np .concatenate ([self ._x_history , x_hist ])[ - MAX_HISTORY_SAMPLE :]
276291 )
277292 self ._y_history = (
278- y_hist
293+ y_hist [ - MAX_HISTORY_SAMPLE :]
279294 if self ._y_history is None
280- else np .concatenate ([self ._y_history , y_hist ])
295+ else np .concatenate ([self ._y_history , y_hist ])[ - MAX_HISTORY_SAMPLE :]
281296 )
282297
283298 def _build_observation (self ) -> np .ndarray :
299+
284300 return compute_observation (
285301 x_history = self ._x_history ,
286302 y_history = self ._y_history ,
0 commit comments