sdesrozis: @vfdev-5 I think the PR could be merged but CI ko...
vfdev-5: Yes, CI is broken now : https://github.com/pytorch/ignite/issues/1287
diff --git a/ignite/contrib/handlers/param_scheduler.py b/ignite/contrib/handlers/param_scheduler.py
index 3d54100a..3a86ec26 100644
--- a/ignite/contrib/handlers/param_scheduler.py
+++ b/ignite/contrib/handlers/param_scheduler.py
@@ -1,3 +1,4 @@
+import itertools
 import math
 import numbers
 import tempfile
@@ -380,8 +381,8 @@ class CosineAnnealingScheduler(CyclicalScheduler):
 
         optimizer = SGD(
             [
-                {"params": model.base.parameters(), 'lr': 0.001),
-                {"params": model.fc.parameters(), 'lr': 0.01),
+                {"params": model.base.parameters(), 'lr': 0.001},
+                {"params": model.fc.parameters(), 'lr': 0.01},
             ]
         )
 
@@ -459,7 +460,7 @@ class ConcatScheduler(ParamScheduler):
             )
 
         for i, scheduler in enumerate(schedulers):
-            if not isinstance(scheduler, ParamScheduler):
+            if not isinstance(scheduler, ParamScheduler) and not isinstance(scheduler, ParamGroupScheduler):
                 raise TypeError(
                     "Value at index {} of schedulers should be a parameter scheduler, "
                     "but given {}".format(i, type(scheduler))
@@ -468,15 +469,29 @@ class ConcatScheduler(ParamScheduler):
         self.schedulers = schedulers
         self.durations = durations
 
-        self.optimizer = self.schedulers[0].optimizer
-        if not (all(id(s.optimizer) == id(self.optimizer) for s in self.schedulers)):
+        param_optimizers = [s.optimizer for s in self.schedulers]
+        param_optimizers = [s if isinstance(s, list) else [s] for s in param_optimizers]
+        param_optimizers = list(itertools.chain(*param_optimizers))
+
+        optimizer = list(set(param_optimizers))
+        if len(optimizer) != 1:
             raise ValueError("schedulers should be related to same optimizer")
 
+        param_names = [s.param_name for s in self.schedulers]
+        param_names = [s if isinstance(s, list) else [s] for s in param_names]
+        param_names = list(itertools.chain(*param_names))
+
+        param_name = list(set(param_names))
+        if len(param_name) != 1:
+            raise ValueError("schedulers should be related to same param_name")
+
         # schedulers should have save_history sync with ParamGroupScheduler
         for s in schedulers:
             s.save_history = save_history
 
-        super(ConcatScheduler, self).__init__(optimizer=self.optimizer, param_name="", save_history=save_history)
+        super(ConcatScheduler, self).__init__(
+            optimizer=optimizer[0], param_name=param_name[0], save_history=save_history
+        )
 
         self._scheduler_index = 0
         self._current_scheduler = None
@@ -530,8 +545,6 @@ class ConcatScheduler(ParamScheduler):
         self._current_duration = (
             self.durations[self._scheduler_index] if self._scheduler_index < len(self.durations) else -1
         )
-        self.param_name = self._current_scheduler.param_name
-        self.optimizer = self._current_scheduler.optimizer
 
     def __call__(self, engine, name=None):
         if self._current_duration == 0:
@@ -581,6 +594,16 @@ class ConcatScheduler(ParamScheduler):
                     "Argument param_names should be list or tuple of strings, but given {}".format(param_names)
                 )
 
+        param_optimizers = [s.optimizer for s in schedulers]
+        param_optimizers = [s if isinstance(s, list) else [s] for s in param_optimizers]
+        param_optimizers = list(itertools.chain(*param_optimizers))
+
+        optimizer = list(set(param_optimizers))
+        if len(optimizer) != 1:
+            raise ValueError("schedulers should be related to same optimizer")
+
+        optimizer = optimizer[0]
+
         # This scheduler uses `ParamScheduler` which
         # should be replicated in order to simulate LR values and
         # not perturb original scheduler.
@@ -588,7 +611,7 @@ class ConcatScheduler(ParamScheduler):
             cache_filepath = Path(tmpdirname) / "ignite_lr_scheduler_cache.pt"
             objs = {"lr_scheduler_{}".format(i): s.state_dict() for i, s in enumerate(schedulers)}
             # all schedulers should be related to the same optimizer
-            objs["optimizer"] = schedulers[0].optimizer.state_dict()
+            objs["optimizer"] = optimizer.state_dict()
 
             torch.save(objs, cache_filepath.as_posix())
 
@@ -611,7 +634,7 @@ class ConcatScheduler(ParamScheduler):
             objs = torch.load(cache_filepath.as_posix())
             for i, s in enumerate(schedulers):
                 s.load_state_dict(objs["lr_scheduler_{}".format(i)])
-                s.optimizer.load_state_dict(objs["optimizer"])
+            optimizer.load_state_dict(objs["optimizer"])
 
             return output
 
@@ -640,7 +663,7 @@ class LRScheduler(ParamScheduler):
         trainer.add_event_handler(Events.ITERATION_COMPLETED, scheduler)
     """
 
-    def __init__(self, lr_scheduler, save_history=False, **kwargs):
+    def __init__(self, lr_scheduler, save_history=False):
 
         if not isinstance(lr_scheduler, _LRScheduler):
             raise TypeError(
@@ -927,7 +950,7 @@ class PiecewiseLinear(ParamScheduler):
         return start_value + (end_value - start_value) * (self.event_index - start_index) / (end_index - start_index)
 
 
-class ParamGroupScheduler(ParamScheduler):
+class ParamGroupScheduler:
     """
     Scheduler helper to group multiple schedulers into one.
 
@@ -981,20 +1004,21 @@ class ParamGroupScheduler(ParamScheduler):
         self.schedulers = schedulers
         self.names = names
 
-        self.optimizer = self.schedulers[0].optimizer
-        if not (all(id(s.optimizer) == id(self.optimizer) for s in schedulers)):
-            raise ValueError("schedulers should be related to same optimizer")
-
         # schedulers should have save_history sync with ParamGroupScheduler
         for s in schedulers:
             s.save_history = save_history
 
-        super(ParamGroupScheduler, self).__init__(optimizer=self.optimizer, param_name="lr", save_history=save_history)
+        self.optimizer = [s.optimizer for s in self.schedulers]
+        self.param_name = [s.param_name for s in self.schedulers]
 
     def __call__(self, engine, name=None):
         for scheduler, name in zip(self.schedulers, self.names):
             scheduler(engine, name)
 
+    @property
+    def optimizer_param_groups(self):
+        return [pg for scheduler in self.schedulers for pg in scheduler.optimizer_param_groups]
+
     @property
     def save_history(self):
         return self.schedulers[0].save_history
@@ -1004,9 +1028,6 @@ class ParamGroupScheduler(ParamScheduler):
         for s in self.schedulers:
             s.save_history = value
 
-    def get_param(self) -> Union[List[float], float]:
-        return [scheduler.get_param() for scheduler in self.schedulers]
-
     def state_dict(self):
         """Returns a dictionary containing a whole state of ParamGroupScheduler.
 
@@ -1077,7 +1098,7 @@ class ParamGroupScheduler(ParamScheduler):
             values = []
             scheduler = cls(schedulers=schedulers, **kwargs)
             for i in range(num_events):
-                params = scheduler.get_param()
+                params = [scheduler.get_param() for scheduler in schedulers]
                 values.append([i] + params)
                 scheduler(engine=None)
 

CycleGAN notebook is broken with updated ParamGroupScheduler
https://github.com/pytorch/ignite/blob/c2c4a368a8666070633242c478c3b4151a05d49d/ignite/contrib/handlers/param_scheduler.py#L960

@sdesrozis above code brakes our cycle gan notebook where we do
```python
gen_lr_scheduler = PiecewiseLinear(optimizer_D, param_name='lr', milestones_values=milestones_values)
desc_lr_scheduler = PiecewiseLinear(optimizer_G, param_name='lr', milestones_values=milestones_values)

lr_scheduler = ParamGroupScheduler([gen_lr_scheduler, desc_lr_scheduler], 
                                   names=['gen_lr_scheduler', 'desc_lr_scheduler'])
```

Can we simply remove that check ?


diff --git a/tests/ignite/contrib/handlers/test_param_scheduler.py b/tests/ignite/contrib/handlers/test_param_scheduler.py
index be314cfa..6005e569 100644
--- a/tests/ignite/contrib/handlers/test_param_scheduler.py
+++ b/tests/ignite/contrib/handlers/test_param_scheduler.py
@@ -339,6 +339,14 @@ def test_concat_scheduler_asserts():
     with pytest.raises(ValueError, match=r"schedulers should be related to same optimizer"):
         ConcatScheduler([scheduler_1, scheduler_3], durations=[30,])
 
+    scheduler_4 = CosineAnnealingScheduler(optimizer, "lr2", start_value=0.0, end_value=1.0, cycle_size=10)
+
+    with pytest.raises(ValueError, match=r"schedulers should be related to same param_name"):
+        ConcatScheduler([scheduler_1, scheduler_4], durations=[30,])
+
+    with pytest.raises(ValueError, match=r"schedulers should be related to same optimizer"):
+        ConcatScheduler.simulate_values(3, [scheduler_1, scheduler_3], durations=[30,])
+
 
 def test_concat_scheduler_state_dict():
     tensor = torch.zeros([1], requires_grad=True)
@@ -814,8 +822,7 @@ def test_simulate_and_plot_values():
         optimizer = None
         event = Events.ITERATION_STARTED
         if scheduler_cls == LRScheduler:
-            scheduler_kwargs["optimizer"] = scheduler_kwargs["lr_scheduler"].optimizer
-            optimizer = scheduler_kwargs["optimizer"]
+            optimizer = scheduler_kwargs["lr_scheduler"].optimizer
             event = Events.ITERATION_COMPLETED
         elif scheduler_cls == ConcatScheduler:
             optimizer = scheduler_kwargs["optimizer"]
@@ -1157,16 +1164,9 @@ def test_param_group_scheduler_asserts():
     ):
         scheduler.load_state_dict({"schedulers": [("a", lr_scheduler1.state_dict()), ("bad_name", {})]})
 
-    optimizer2 = torch.optim.SGD([{"params": t1, "lr": 0.1}, {"params": t2, "lr": 0.1}])
-    lr_scheduler3 = LinearCyclicalScheduler(
-        optimizer2, "lr", param_group_index=0, start_value=1.0, end_value=0.0, cycle_size=10
-    )
-    with pytest.raises(ValueError, match=r"schedulers should be related to same optimizer"):
-        ParamGroupScheduler(schedulers=[lr_scheduler1, lr_scheduler3])
-
 
 def test_param_group_scheduler():
-    def _test(lr_schedulers, optimizer):
+    def _test(lr_schedulers, save_lr):
         num_iterations = 10
         max_epochs = 20
 
@@ -1175,17 +1175,15 @@ def test_param_group_scheduler():
 
         trainer = Engine(lambda engine, batch: None)
 
-        @trainer.on(Events.ITERATION_COMPLETED)
-        def save_lr(engine):
-            lrs.append((optimizer.param_groups[0]["lr"], optimizer.param_groups[1]["lr"]))
-
         trainer.add_event_handler(Events.ITERATION_STARTED, scheduler)
 
         data = [0] * num_iterations
 
         for _ in range(2):
             lrs = []
+            trainer.add_event_handler(Events.ITERATION_COMPLETED, save_lr, lrs)
             trainer.run(data, max_epochs=max_epochs)
+            trainer.remove_event_handler(save_lr, Events.ITERATION_COMPLETED)
             assert [lr[0] for lr in lrs] == pytest.approx([lr[1] for lr in lrs])
             scheduler.load_state_dict(state_dict)
 
@@ -1203,7 +1201,24 @@ def test_param_group_scheduler():
     lr_scheduler2 = LinearCyclicalScheduler(
         optimizer, "lr", param_group_index=1, start_value=1.0, end_value=0.0, cycle_size=10
     )
-    _test([lr_scheduler1, lr_scheduler2], optimizer)
+
+    def save_lr_one_optimizer(engine, lrs):
+        lrs.append((optimizer.param_groups[0]["lr"], optimizer.param_groups[1]["lr"]))
+
+    _test([lr_scheduler1, lr_scheduler2], save_lr_one_optimizer)
+
+    t1 = torch.zeros([1], requires_grad=True)
+    optimizer_1 = torch.optim.SGD(params=[t1], lr=0.1)
+    t2 = torch.zeros([1], requires_grad=True)
+    optimizer_2 = torch.optim.SGD(params=[t2], lr=0.1)
+
+    lr_scheduler1 = LinearCyclicalScheduler(optimizer_1, "lr", start_value=1.0, end_value=0.0, cycle_size=10)
+    lr_scheduler2 = LinearCyclicalScheduler(optimizer_2, "lr", start_value=1.0, end_value=0.0, cycle_size=10)
+
+    def save_lr_mutliple_optimizers(engine, lrs):
+        lrs.append((optimizer_1.param_groups[0]["lr"], optimizer_2.param_groups[0]["lr"]))
+
+    _test([lr_scheduler1, lr_scheduler2], save_lr_mutliple_optimizers)
 
 
 def test_scheduler_with_param_groups():

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc pip install torch torchvision -f https://download.pytorch.org/whl/cpu/torch_stable.html -U
3.7
requirements-dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
absl-py==2.1.0
alembic==1.12.1
arrow==1.2.3
attrs==24.2.0
boto3==1.33.13
botocore==1.33.13
bravado==11.1.0
bravado-core==6.1.1
cached-property==1.5.2
cachetools==5.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
click==7.1.2
cloudpickle==2.2.1
codecov==2.1.13
coverage==7.2.7
cycler==0.11.0
databricks-cli==0.18.0
dill==0.3.7
docker==6.1.3
docker-pycreds==0.4.0
entrypoints==0.4
exceptiongroup==1.2.2
execnet==2.0.2
Flask==1.1.4
fonttools==4.38.0
fqdn==1.5.1
funcsigs==1.0.2
furl==2.1.4
future==1.0.0
gitdb==4.0.12
GitPython==3.1.44
google-auth==2.38.0
google-auth-oauthlib==0.4.6
greenlet==3.1.1
grpcio==1.62.3
gunicorn==20.1.0
gym==0.26.2
gym-notices==0.0.8
hestia==0.6.0
humanfriendly==10.0
idna==3.10
imageio==2.31.2
importlib-metadata==5.2.0
importlib-resources==5.12.0
iniconfig==2.0.0
isoduration==20.11.0
itsdangerous==1.1.0
Jinja2==2.10.3
jmespath==1.0.1
joblib==1.3.2
jsonpatch==1.33
jsonpointer==3.0.0
jsonref==1.1.0
jsonschema==4.17.3
kiwisolver==1.4.5
Mako==1.2.4
Markdown==3.4.4
MarkupSafe==2.1.5
marshmallow==3.0.0rc5
matplotlib==3.5.3
mlflow==1.30.1
monotonic==1.6
msgpack==1.0.5
neptune-client==1.11.1
networkx==2.6.3
numpy==1.21.6
oauthlib==3.2.2
orderedmultidict==1.0.1
packaging==21.3
pandas==1.3.5
pathlib2==2.3.7.post1
Pillow==9.5.0
pkgutil_resolve_name==1.3.10
platformdirs==4.0.0
pluggy==1.2.0
polyaxon-client==0.6.1
polyaxon-schemas==0.6.1
polystores==0.2.5
prometheus-client==0.17.1
prometheus_flask_exporter==0.23.2
protobuf==3.20.3
psutil==7.0.0
pyasn1==0.5.1
pyasn1-modules==0.3.0
PyJWT==2.8.0
pynvml==11.5.3
pyparsing==3.1.4
pyrsistent==0.19.3
pytest==7.4.4
pytest-cov==4.1.0
pytest-xdist==3.5.0
python-dateutil==2.9.0.post0
-e git+https://github.com/pytorch/ignite.git@51fc9cab64f75f893963b2f44536d8eef6df6ac3#egg=pytorch_ignite
pytz==2022.7.1
PyWavelets==1.3.0
PyYAML==6.0.1
querystring-parser==1.2.4
requests==2.31.0
requests-file==2.1.0
requests-oauthlib==2.0.0
requests-toolbelt==1.0.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rhea==0.5.5
rsa==4.9
s3transfer==0.8.2
scikit-image==0.19.3
scikit-learn==1.0.2
scipy==1.7.3
sentry-sdk==2.24.1
setproctitle==1.3.3
simplejson==3.20.1
six==1.17.0
smmap==5.0.2
SQLAlchemy==1.4.54
sqlparse==0.4.4
swagger-spec-validator==3.0.3
tabulate==0.9.0
tensorboard==2.11.2
tensorboard-data-server==0.6.1
tensorboard-plugin-wit==1.8.1
tensorboardX==2.6.2.2
threadpoolctl==3.1.0
tifffile==2021.11.2
tomli==2.0.1
torch==1.13.1+cpu
torchvision==0.14.1+cpu
tornado==6.2
tqdm==4.48.0
trains==0.16.4
typing_extensions==4.7.1
uri-template==1.3.0
urllib3==1.26.20
visdom==0.2.4
wandb==0.18.7
webcolors==1.13
websocket-client==1.6.1
Werkzeug==1.0.1
zipp==3.15.0

name: ignite
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - absl-py==2.1.0
      - alembic==1.12.1
      - arrow==1.2.3
      - attrs==24.2.0
      - boto3==1.33.13
      - botocore==1.33.13
      - bravado==11.1.0
      - bravado-core==6.1.1
      - cached-property==1.5.2
      - cachetools==5.5.2
      - charset-normalizer==3.4.1
      - click==7.1.2
      - cloudpickle==2.2.1
      - codecov==2.1.13
      - coverage==7.2.7
      - cycler==0.11.0
      - databricks-cli==0.18.0
      - dill==0.3.7
      - docker==6.1.3
      - docker-pycreds==0.4.0
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - execnet==2.0.2
      - flask==1.1.4
      - fonttools==4.38.0
      - fqdn==1.5.1
      - funcsigs==1.0.2
      - furl==2.1.4
      - future==1.0.0
      - gitdb==4.0.12
      - gitpython==3.1.44
      - google-auth==2.38.0
      - google-auth-oauthlib==0.4.6
      - greenlet==3.1.1
      - grpcio==1.62.3
      - gunicorn==20.1.0
      - gym==0.26.2
      - gym-notices==0.0.8
      - hestia==0.6.0
      - humanfriendly==10.0
      - idna==3.10
      - imageio==2.31.2
      - importlib-metadata==5.2.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - isoduration==20.11.0
      - itsdangerous==1.1.0
      - jinja2==2.10.3
      - jmespath==1.0.1
      - joblib==1.3.2
      - jsonpatch==1.33
      - jsonpointer==3.0.0
      - jsonref==1.1.0
      - jsonschema==4.17.3
      - kiwisolver==1.4.5
      - mako==1.2.4
      - markdown==3.4.4
      - markupsafe==2.1.5
      - marshmallow==3.0.0rc5
      - matplotlib==3.5.3
      - mlflow==1.30.1
      - monotonic==1.6
      - msgpack==1.0.5
      - neptune-client==1.11.1
      - networkx==2.6.3
      - numpy==1.21.6
      - oauthlib==3.2.2
      - orderedmultidict==1.0.1
      - packaging==21.3
      - pandas==1.3.5
      - pathlib2==2.3.7.post1
      - pillow==9.5.0
      - pkgutil-resolve-name==1.3.10
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - polyaxon-client==0.6.1
      - polyaxon-schemas==0.6.1
      - polystores==0.2.5
      - prometheus-client==0.17.1
      - prometheus-flask-exporter==0.23.2
      - protobuf==3.20.3
      - psutil==7.0.0
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pyjwt==2.8.0
      - pynvml==11.5.3
      - pyparsing==3.1.4
      - pyrsistent==0.19.3
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - pytest-xdist==3.5.0
      - python-dateutil==2.9.0.post0
      - pytorch-ignite==0.5.0
      - pytz==2022.7.1
      - pywavelets==1.3.0
      - pyyaml==6.0.1
      - querystring-parser==1.2.4
      - requests==2.31.0
      - requests-file==2.1.0
      - requests-oauthlib==2.0.0
      - requests-toolbelt==1.0.0
      - rfc3339-validator==0.1.4
      - rfc3986-validator==0.1.1
      - rhea==0.5.5
      - rsa==4.9
      - s3transfer==0.8.2
      - scikit-image==0.19.3
      - scikit-learn==1.0.2
      - scipy==1.7.3
      - sentry-sdk==2.24.1
      - setproctitle==1.3.3
      - simplejson==3.20.1
      - six==1.17.0
      - smmap==5.0.2
      - sqlalchemy==1.4.54
      - sqlparse==0.4.4
      - swagger-spec-validator==3.0.3
      - tabulate==0.9.0
      - tensorboard==2.11.2
      - tensorboard-data-server==0.6.1
      - tensorboard-plugin-wit==1.8.1
      - tensorboardx==2.6.2.2
      - threadpoolctl==3.1.0
      - tifffile==2021.11.2
      - tomli==2.0.1
      - torch==1.13.1+cpu
      - torchvision==0.14.1+cpu
      - tornado==6.2
      - tqdm==4.48.0
      - trains==0.16.4
      - typing-extensions==4.7.1
      - uri-template==1.3.0
      - urllib3==1.26.20
      - visdom==0.2.4
      - wandb==0.18.7
      - webcolors==1.13
      - websocket-client==1.6.1
      - werkzeug==1.0.1
      - zipp==3.15.0
prefix: /opt/conda/envs/ignite

tests/ignite/contrib/handlers/test_param_scheduler.py::test_concat_scheduler_asserts tests/ignite/contrib/handlers/test_param_scheduler.py::test_param_group_scheduler
tests/ignite/contrib/handlers/test_param_scheduler.py::test_param_scheduler_asserts tests/ignite/contrib/handlers/test_param_scheduler.py::test_linear_scheduler tests/ignite/contrib/handlers/test_param_scheduler.py::test_linear_scheduler_cycle_size_two tests/ignite/contrib/handlers/test_param_scheduler.py::test_cosine_annealing_scheduler tests/ignite/contrib/handlers/test_param_scheduler.py::test_concat_scheduler_state_dict tests/ignite/contrib/handlers/test_param_scheduler.py::test_concat_scheduler_two_schedulers tests/ignite/contrib/handlers/test_param_scheduler.py::test_concat_scheduler_two_linear tests/ignite/contrib/handlers/test_param_scheduler.py::test_concat_scheduler_3_schedulers tests/ignite/contrib/handlers/test_param_scheduler.py::test_save_param_history tests/ignite/contrib/handlers/test_param_scheduler.py::test_lr_scheduler_asserts tests/ignite/contrib/handlers/test_param_scheduler.py::test_lr_scheduler tests/ignite/contrib/handlers/test_param_scheduler.py::test_piecewiselinear_asserts tests/ignite/contrib/handlers/test_param_scheduler.py::test_piecewiselinear tests/ignite/contrib/handlers/test_param_scheduler.py::test_simulate_and_plot_values tests/ignite/contrib/handlers/test_param_scheduler.py::test_create_lr_scheduler_with_warmup tests/ignite/contrib/handlers/test_param_scheduler.py::test_create_lr_scheduler_with_warmup_on_combined_scheduler tests/ignite/contrib/handlers/test_param_scheduler.py::test_create_lr_scheduler_with_warmup_with_real_model tests/ignite/contrib/handlers/test_param_scheduler.py::test_param_group_scheduler_asserts tests/ignite/contrib/handlers/test_param_scheduler.py::test_scheduler_with_param_groups tests/ignite/contrib/handlers/test_param_scheduler.py::test_lr_scheduling_on_non_torch_optimizers
ignite/contrib/handlers/param_scheduler.py
ignite/contrib/handlers/param_scheduler.py