1313# See the License for the specific language governing permissions and
1414# limitations under the License.
1515import torch
16+ from absl import flags
1617from absl .testing import absltest , parameterized
1718
1819from emerging_optimizers .scalar_optimizers import (
2324)
2425
2526
27+ # Define command line flags
28+ flags .DEFINE_string ("device" , "cpu" , "Device to run tests on: 'cpu', 'cuda', or 'auto'" )
29+ flags .DEFINE_integer ("seed" , 42 , "Random seed for reproducible tests" )
30+ flags .DEFINE_boolean ("skip_gpu_tests" , False , "Skip GPU tests even if CUDA is available" )
31+
32+ FLAGS = flags .FLAGS
33+
34+
2635# Base class for tests requiring seeding for determinism
2736class BaseTestCase (parameterized .TestCase ):
2837 def setUp (self ):
29- """Set random seed before each test."""
30- # Set seed for PyTorch
31- torch .manual_seed (42 )
38+ """Set random seed and device before each test."""
39+ # Set seed for PyTorch (using seed from flags)
40+ torch .manual_seed (FLAGS . seed )
3241 # Set seed for CUDA if available
3342 if torch .cuda .is_available ():
34- torch .cuda .manual_seed_all (42 )
43+ torch .cuda .manual_seed_all (FLAGS .seed )
44+
45+ # Set up device based on flags
46+ self .device = self ._get_test_device ()
47+
48+ def _get_test_device (self ):
49+ """Get the device to use for testing based on flags."""
50+ if FLAGS .device == "auto" :
51+ return "cuda" if torch .cuda .is_available () and not FLAGS .skip_gpu_tests else "cpu"
52+ elif FLAGS .device == "cuda" :
53+ if not torch .cuda .is_available ():
54+ self .skipTest ("CUDA not available" )
55+ if FLAGS .skip_gpu_tests :
56+ self .skipTest ("GPU tests skipped by flag" )
57+ return "cuda"
58+ else :
59+ return "cpu"
60+
61+ def _move_to_device (self , * tensors ):
62+ """Helper method to move tensors to the test device."""
63+ return tuple (tensor .to (self .device ) for tensor in tensors )
3564
3665
3766class ScalarOptimizerTest (BaseTestCase ):
3867 def test_calculate_adam_update_simple (self ) -> None :
3968 exp_avg_initial = torch .tensor ([[1.0 ]])
4069 exp_avg_sq_initial = torch .tensor ([[2.0 ]])
4170 grad = torch .tensor ([[0.5 ]])
71+
72+ # Move tensors to the test device
73+ exp_avg_initial , exp_avg_sq_initial , grad = self ._move_to_device (exp_avg_initial , exp_avg_sq_initial , grad )
74+
4275 betas = (0.9 , 0.99 )
4376 eps = 1e-8
4477 step = 10
@@ -59,7 +92,7 @@ def test_calculate_adam_update_simple(self) -> None:
5992 eps = eps ,
6093 )
6194
62- initial_param_val_tensor = torch .tensor ([[10.0 ]])
95+ initial_param_val_tensor = torch .tensor ([[10.0 ]]). to ( self . device )
6396 param = torch .nn .Parameter (initial_param_val_tensor .clone ())
6497 param .grad = grad .clone ()
6598
@@ -249,6 +282,23 @@ def test_calculate_sim_ademamix_update_with_zero_momentum_and_alpha_equals_rmspr
249282 expected_param_val_after_step = initial_param_val_tensor - lr * sim_ademamix_update
250283 torch .testing .assert_close (param .data , expected_param_val_after_step , atol = 1e-6 , rtol = 1e-6 )
251284
285+ def test_device_functionality (self ) -> None :
286+ """Test that tensors are correctly moved to the specified device."""
287+ # Create test tensors
288+ tensor1 = torch .tensor ([1.0 , 2.0 , 3.0 ])
289+ tensor2 = torch .tensor ([[1.0 ], [2.0 ]])
290+
291+ # Move to test device
292+ tensor1_device , tensor2_device = self ._move_to_device (tensor1 , tensor2 )
293+
294+ # Verify they are on the correct device
295+ self .assertEqual (str (tensor1_device .device ), self .device )
296+ self .assertEqual (str (tensor2_device .device ), self .device )
297+
298+ # Verify values are preserved
299+ torch .testing .assert_close (tensor1_device .cpu (), tensor1 , atol = 1e-6 , rtol = 1e-6 )
300+ torch .testing .assert_close (tensor2_device .cpu (), tensor2 , atol = 1e-6 , rtol = 1e-6 )
301+
252302
253303if __name__ == "__main__" :
254304 absltest .main ()
0 commit comments