Repository: pytorch/ignite

Issue:
Dynamic Typing
Sometimes there are types assumed and hardcoded, where it is not necessary.

For example there is [this part](https://github.com/pytorch/ignite/blob/master/ignite/engine/engine.py#L971) where we should infer the previous type of the data loader and re-use this. 
Other examples include type specific operations. E.g. we often check for mappings in general but then we only create dicts after doing some operations without restoring the original type. 

I will go through the core package and change this.

Discussion:
justusschock: there is one test, that needs to be adapted, I will take care of this

Patch:
diff --git a/ignite/engine/engine.py b/ignite/engine/engine.py
index e6131c4b..15a443fa 100644
--- a/ignite/engine/engine.py
+++ b/ignite/engine/engine.py
@@ -974,7 +974,7 @@ def _update_dataloader(dataloader, new_batch_sampler):
             params_keys.remove(k)
     params = {k: getattr(dataloader, k) for k in params_keys}
     params['batch_sampler'] = new_batch_sampler
-    return torch.utils.data.DataLoader(**params)
+    return type(dataloader)(**params)
 
 
 class ReproducibleBatchSampler(torch.utils.data.sampler.BatchSampler):
diff --git a/ignite/utils.py b/ignite/utils.py
index 1ea60149..381a500f 100644
--- a/ignite/utils.py
+++ b/ignite/utils.py
@@ -34,9 +34,9 @@ def apply_to_type(input_, input_type, func):
     elif isinstance(input_, (str, bytes)):
         return input_
     elif isinstance(input_, collections.Mapping):
-        return {k: apply_to_type(sample, input_type, func) for k, sample in input_.items()}
+        return type(input_)({k: apply_to_type(sample, input_type, func) for k, sample in input_.items()})
     elif isinstance(input_, collections.Sequence):
-        return [apply_to_type(sample, input_type, func) for sample in input_]
+        return type(input_)([apply_to_type(sample, input_type, func) for sample in input_])
     else:
         raise TypeError(("input must contain {}, dicts or lists; found {}"
                          .format(input_type, type(input_))))


Test patch:
diff --git a/tests/ignite/test_utils.py b/tests/ignite/test_utils.py
index 155f96fd..db352774 100644
--- a/tests/ignite/test_utils.py
+++ b/tests/ignite/test_utils.py
@@ -19,12 +19,18 @@ def test_convert_tensor():
     tensor = convert_tensor(x, device='cpu', non_blocking=False)
     assert torch.is_tensor(tensor)
 
-    x = (torch.tensor([0.0]), torch.tensor([0.0]))
+    x = [torch.tensor([0.0]), torch.tensor([0.0])]
     list_ = convert_tensor(x)
     assert isinstance(list_, list)
     assert torch.is_tensor(list_[0])
     assert torch.is_tensor(list_[1])
 
+    x = (torch.tensor([0.0]), torch.tensor([0.0]))
+    tuple_ = convert_tensor(x)
+    assert isinstance(tuple_, tuple)
+    assert torch.is_tensor(tuple_[0])
+    assert torch.is_tensor(tuple_[1])
+
     x = {'a': torch.tensor([0.0]), 'b': torch.tensor([0.0])}
     dict_ = convert_tensor(x)
     assert isinstance(dict_, dict)