11"""Tests for continuous solvers (CEM, MPPI, GD, Nevergrad)."""
22
33import numpy as np
4- import pytest
54import torch
65from gymnasium import spaces as gym_spaces
76
1514class DummyCostModel :
1615 """Simple Costable implementation for tests."""
1716
18- def get_cost (self , info_dict : dict , action_candidates : torch .Tensor ) -> torch .Tensor :
17+ def get_cost (
18+ self , info_dict : dict , action_candidates : torch .Tensor
19+ ) -> torch .Tensor :
1920 # Quadratic cost: sum over horizon and action dims
2021 cost = action_candidates .pow (2 ).sum (dim = (- 1 , - 2 ))
2122 return cost
@@ -39,7 +40,9 @@ def test_cem_solver_configure():
3940 """Test CEMSolver configuration."""
4041 model = DummyCostModel ()
4142 solver = CEMSolver (model = model , n_steps = 10 )
42- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32 )
43+ action_space = gym_spaces .Box (
44+ low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32
45+ )
4346 config = PlanConfig (horizon = 5 , receding_horizon = 3 , action_block = 1 )
4447
4548 solver .configure (action_space = action_space , n_envs = 2 , config = config )
@@ -58,48 +61,60 @@ def test_cem_solver_configure_discrete_warning(caplog):
5861 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
5962
6063 solver .configure (action_space = action_space , n_envs = 1 , config = config )
61- assert "discrete" in caplog .text .lower () or solver ._configured # warning logged
64+ assert (
65+ 'discrete' in caplog .text .lower () or solver ._configured
66+ ) # warning logged
6267
6368
6469def test_cem_solver_init_action_distrib ():
6570 """Test CEMSolver action distribution initialization."""
6671 model = DummyCostModel ()
6772 solver = CEMSolver (model = model , n_steps = 10 )
68- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32 )
73+ action_space = gym_spaces .Box (
74+ low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32
75+ )
6976 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
7077 solver .configure (action_space = action_space , n_envs = 2 , config = config )
7178
72- mean , var = solver .init_action_distrib ()
79+ actions = torch .zeros (2 , 5 , 3 )
80+ mean , var = solver .init_action_distrib (actions )
7381 assert mean .shape == (2 , 5 , 3 )
7482 assert var .shape == (2 , 5 , 3 )
7583
7684
7785def test_cem_solver_init_action_distrib_with_init ():
78- """Test CEMSolver action distribution with initial actions."""
86+ """Test CEMSolver action distribution returns provided actions as mean ."""
7987 model = DummyCostModel ()
8088 solver = CEMSolver (model = model , n_steps = 10 )
81- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32 )
89+ action_space = gym_spaces .Box (
90+ low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32
91+ )
8292 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
8393 solver .configure (action_space = action_space , n_envs = 2 , config = config )
8494
85- init_actions = torch .randn (2 , 2 , 3 )
95+ init_actions = torch .randn (2 , 5 , 3 )
8696 mean , var = solver .init_action_distrib (init_actions )
87- assert mean .shape == (2 , 5 , 3 ) # Padded to horizon
97+ assert mean .shape == (2 , 5 , 3 )
98+ assert mean is init_actions
8899
89100
90101def test_cem_solver_call ():
91102 """Test CEMSolver __call__ method."""
92103 model = DummyCostModel ()
93- solver = CEMSolver (model = model , n_steps = 2 , num_samples = 50 , batch_size = 2 , topk = 10 )
94- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32 )
104+ solver = CEMSolver (
105+ model = model , n_steps = 2 , num_samples = 50 , batch_size = 2 , topk = 10
106+ )
107+ action_space = gym_spaces .Box (
108+ low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32
109+ )
95110 config = PlanConfig (horizon = 3 , receding_horizon = 2 )
96111 solver .configure (action_space = action_space , n_envs = 2 , config = config )
97112
98- info_dict = {" pixels" : torch .randn (2 , 1 , 3 , 64 , 64 )}
113+ info_dict = {' pixels' : torch .randn (2 , 1 , 3 , 64 , 64 )}
99114 outputs = solver (info_dict )
100115
101- assert " actions" in outputs
102- assert outputs [" actions" ].shape == (2 , 3 , 2 )
116+ assert ' actions' in outputs
117+ assert outputs [' actions' ].shape == (2 , 3 , 2 )
103118
104119
105120###########################
@@ -110,7 +125,9 @@ def test_cem_solver_call():
110125def test_icem_solver_init ():
111126 """Test ICEMSolver initialization."""
112127 model = DummyCostModel ()
113- solver = ICEMSolver (model = model , n_steps = 10 , num_samples = 100 , noise_beta = 2.0 )
128+ solver = ICEMSolver (
129+ model = model , n_steps = 10 , num_samples = 100 , noise_beta = 2.0
130+ )
114131 assert solver .model is model
115132 assert solver .n_steps == 10
116133 assert solver .num_samples == 100
@@ -123,7 +140,9 @@ def test_icem_solver_configure():
123140 """Test ICEMSolver configuration."""
124141 model = DummyCostModel ()
125142 solver = ICEMSolver (model = model , n_steps = 10 )
126- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32 )
143+ action_space = gym_spaces .Box (
144+ low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32
145+ )
127146 config = PlanConfig (horizon = 5 , receding_horizon = 3 , action_block = 1 )
128147
129148 solver .configure (action_space = action_space , n_envs = 2 , config = config )
@@ -140,43 +159,59 @@ def test_icem_solver_init_action_distrib():
140159 """Test ICEMSolver action distribution initialization."""
141160 model = DummyCostModel ()
142161 solver = ICEMSolver (model = model , n_steps = 10 )
143- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32 )
162+ action_space = gym_spaces .Box (
163+ low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32
164+ )
144165 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
145166 solver .configure (action_space = action_space , n_envs = 2 , config = config )
146167
147- mean , var = solver .init_action_distrib ()
168+ actions = torch .zeros (2 , 5 , 3 )
169+ mean , var = solver .init_action_distrib (actions )
148170 assert mean .shape == (2 , 5 , 3 )
149171 assert var .shape == (2 , 5 , 3 )
150172
151173
152174def test_icem_solver_call ():
153175 """Test ICEMSolver __call__ method."""
154176 model = DummyCostModel ()
155- solver = ICEMSolver (model = model , n_steps = 2 , num_samples = 50 , batch_size = 2 , topk = 10 )
156- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32 )
177+ solver = ICEMSolver (
178+ model = model , n_steps = 2 , num_samples = 50 , batch_size = 2 , topk = 10
179+ )
180+ action_space = gym_spaces .Box (
181+ low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32
182+ )
157183 config = PlanConfig (horizon = 3 , receding_horizon = 2 )
158184 solver .configure (action_space = action_space , n_envs = 2 , config = config )
159185
160- info_dict = {" pixels" : torch .randn (2 , 1 , 3 , 64 , 64 )}
186+ info_dict = {' pixels' : torch .randn (2 , 1 , 3 , 64 , 64 )}
161187 outputs = solver (info_dict )
162188
163- assert " actions" in outputs
164- assert outputs [" actions" ].shape == (2 , 3 , 2 )
189+ assert ' actions' in outputs
190+ assert outputs [' actions' ].shape == (2 , 3 , 2 )
165191
166192
167193def test_icem_solver_white_noise_fallback ():
168194 """Test ICEMSolver with beta=0 (white noise, equivalent to standard CEM)."""
169195 model = DummyCostModel ()
170- solver = ICEMSolver (model = model , n_steps = 2 , num_samples = 50 , batch_size = 2 , topk = 10 , noise_beta = 0.0 )
171- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32 )
196+ solver = ICEMSolver (
197+ model = model ,
198+ n_steps = 2 ,
199+ num_samples = 50 ,
200+ batch_size = 2 ,
201+ topk = 10 ,
202+ noise_beta = 0.0 ,
203+ )
204+ action_space = gym_spaces .Box (
205+ low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32
206+ )
172207 config = PlanConfig (horizon = 3 , receding_horizon = 2 )
173208 solver .configure (action_space = action_space , n_envs = 2 , config = config )
174209
175- info_dict = {" pixels" : torch .randn (2 , 1 , 3 , 64 , 64 )}
210+ info_dict = {' pixels' : torch .randn (2 , 1 , 3 , 64 , 64 )}
176211 outputs = solver (info_dict )
177212
178- assert " actions" in outputs
179- assert outputs [" actions" ].shape == (2 , 3 , 2 )
213+ assert ' actions' in outputs
214+ assert outputs [' actions' ].shape == (2 , 3 , 2 )
180215
181216
182217###########################
@@ -197,7 +232,9 @@ def test_mppi_solver_configure():
197232 """Test MPPISolver configuration."""
198233 model = DummyCostModel ()
199234 solver = MPPISolver (model = model , n_steps = 10 )
200- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32 )
235+ action_space = gym_spaces .Box (
236+ low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32
237+ )
201238 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
202239
203240 solver .configure (action_space = action_space , n_envs = 2 , config = config )
@@ -212,11 +249,14 @@ def test_mppi_solver_init_action_distrib():
212249 """Test MPPISolver action distribution initialization."""
213250 model = DummyCostModel ()
214251 solver = MPPISolver (model = model , n_steps = 10 )
215- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32 )
252+ action_space = gym_spaces .Box (
253+ low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32
254+ )
216255 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
217256 solver .configure (action_space = action_space , n_envs = 2 , config = config )
218257
219- mean , var = solver .init_action_distrib ()
258+ actions = torch .zeros (2 , 5 , 3 )
259+ mean , var = solver .init_action_distrib (actions )
220260 assert mean .shape == (2 , 5 , 3 )
221261 assert var .shape == (2 , 5 , 3 )
222262
@@ -225,15 +265,17 @@ def test_mppi_solver_call():
225265 """Test MPPISolver __call__ method."""
226266 model = DummyCostModel ()
227267 solver = MPPISolver (model = model , n_steps = 2 , num_samples = 10 , batch_size = 2 )
228- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32 )
268+ action_space = gym_spaces .Box (
269+ low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32
270+ )
229271 config = PlanConfig (horizon = 3 , receding_horizon = 2 )
230272 solver .configure (action_space = action_space , n_envs = 2 , config = config )
231273
232- info_dict = {" pixels" : torch .randn (2 , 1 , 3 , 64 , 64 )}
274+ info_dict = {' pixels' : torch .randn (2 , 1 , 3 , 64 , 64 )}
233275 outputs = solver (info_dict )
234276
235- assert " actions" in outputs
236- assert outputs [" actions" ].shape == (2 , 3 , 2 )
277+ assert ' actions' in outputs
278+ assert outputs [' actions' ].shape == (2 , 3 , 2 )
237279
238280
239281###########################
@@ -254,7 +296,9 @@ def test_gradient_solver_configure():
254296 """Test GradientSolver configuration."""
255297 model = DummyCostModel ()
256298 solver = GradientSolver (model = model , n_steps = 10 )
257- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32 )
299+ action_space = gym_spaces .Box (
300+ low = - 1 , high = 1 , shape = (4 , 2 ), dtype = np .float32
301+ )
258302 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
259303
260304 solver .configure (action_space = action_space , n_envs = 2 , config = config )
@@ -269,25 +313,36 @@ def test_gradient_solver_init_action():
269313 """Test GradientSolver action initialization."""
270314 model = DummyCostModel ()
271315 solver = GradientSolver (model = model , n_steps = 10 , num_samples = 3 )
272- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32 )
316+ action_space = gym_spaces .Box (
317+ low = - 1 , high = 1 , shape = (2 , 3 ), dtype = np .float32
318+ )
273319 config = PlanConfig (horizon = 5 , receding_horizon = 3 )
274320 solver .configure (action_space = action_space , n_envs = 2 , config = config )
275321
276- solver .init_action ()
277- assert hasattr (solver , "init" )
278- assert solver .init .shape == (2 , 3 , 5 , 3 ) # (n_envs, num_samples, horizon, action_dim)
322+ solver .init_action (torch .zeros (2 , 5 , 3 ))
323+ assert hasattr (solver , 'init' )
324+ assert solver .init .shape == (
325+ 2 ,
326+ 3 ,
327+ 5 ,
328+ 3 ,
329+ ) # (n_envs, num_samples, horizon, action_dim)
279330
280331
281332def test_gradient_solver_call ():
282333 """Test GradientSolver __call__ method."""
283334 model = DummyCostModel ()
284- solver = GradientSolver (model = model , n_steps = 2 , num_samples = 2 , batch_size = 2 )
285- action_space = gym_spaces .Box (low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32 )
335+ solver = GradientSolver (
336+ model = model , n_steps = 2 , num_samples = 2 , batch_size = 2
337+ )
338+ action_space = gym_spaces .Box (
339+ low = - 1 , high = 1 , shape = (2 , 2 ), dtype = np .float32
340+ )
286341 config = PlanConfig (horizon = 3 , receding_horizon = 2 )
287342 solver .configure (action_space = action_space , n_envs = 2 , config = config )
288343
289- info_dict = {" pixels" : torch .randn (2 , 1 , 3 , 64 , 64 )}
344+ info_dict = {' pixels' : torch .randn (2 , 1 , 3 , 64 , 64 )}
290345 outputs = solver (info_dict )
291346
292- assert " actions" in outputs
293- assert outputs [" actions" ].shape == (2 , 3 , 2 )
347+ assert ' actions' in outputs
348+ assert outputs [' actions' ].shape == (2 , 3 , 2 )
0 commit comments