Repository: mlcommons/GaNDLF

Issue:
import GANDLF.models produces circular import
**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. initialize virtual env. with GANLDF installed
2. python
3. `>>> import GANDLF.models`
4. See error:

```python-traceback
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/edwardsb/repositories/GaNDLF/GANDLF/models/__init__.py", line 1, in <module>
    from .unet import unet, resunet
  File "/home/edwardsb/repositories/GaNDLF/GANDLF/models/unet.py", line 12, in <module>
    from .modelBase import ModelBase
  File "/home/edwardsb/repositories/GaNDLF/GANDLF/models/modelBase.py", line 8, in <module>
    from GANDLF.utils import get_linear_interpolation_mode
  File "/home/edwardsb/repositories/GaNDLF/GANDLF/utils/__init__.py", line 27, in <module>
    from .parameter_processing import (
  File "/home/edwardsb/repositories/GaNDLF/GANDLF/utils/parameter_processing.py", line 1, in <module>
    from GANDLF.models.modelBase import ModelBase
ImportError: cannot import name 'ModelBase' from partially initialized module 'GANDLF.models.modelBase' 
```
(most likely due to a circular import) (/home/edwardsb/repositories/GaNDLF/GANDLF/models/modelBase.py)

**Expected behavior**
import executes cleanly without an exception being raised

**Screenshots**
If applicable, add screenshots to help explain your problem.

**GaNDLF Version**
0.0.14.dev0 master branch



Discussion:

Resolving patch:
diff --git a/GANDLF/models/modelBase.py b/GANDLF/models/modelBase.py
--- a/GANDLF/models/modelBase.py
+++ b/GANDLF/models/modelBase.py
@@ -6,6 +6,7 @@
 import torch.nn.functional as F
 
 from GANDLF.utils import get_linear_interpolation_mode
+from GANDLF.utils.modelbase import get_modelbase_final_layer
 from GANDLF.models.seg_modules.average_pool import (
     GlobalAveragePooling3D,
     GlobalAveragePooling2D,
@@ -78,35 +79,7 @@ def __init__(self, parameters):
             self.Norm = self.get_norm_type(self.norm_type.lower(), self.n_dimensions)
 
     def get_final_layer(self, final_convolution_layer):
-        """
-        This function gets the final layer of the model.
-
-        Args:
-            final_convolution_layer (str): The final layer of the model as a string.
-
-        Returns:
-            Functional: sigmoid, softmax, or None
-        """
-        none_list = [
-            "none",
-            None,
-            "None",
-            "regression",
-            "classification_but_not_softmax",
-            "logits",
-            "classification_without_softmax",
-        ]
-
-        if final_convolution_layer in ["sigmoid", "sig"]:
-            final_convolution_layer = torch.sigmoid
-
-        elif final_convolution_layer in ["softmax", "soft"]:
-            final_convolution_layer = F.softmax
-
-        elif final_convolution_layer in none_list:
-            final_convolution_layer = None
-
-        return final_convolution_layer
+        return get_modelbase_final_layer(final_convolution_layer)
 
     def get_norm_type(self, norm_type, dimensions):
         """
diff --git a/GANDLF/utils/modelbase.py b/GANDLF/utils/modelbase.py
new file mode 100644
--- /dev/null
+++ b/GANDLF/utils/modelbase.py
@@ -0,0 +1,34 @@
+import torch
+import torch.nn.functional as F
+
+
+def get_modelbase_final_layer(final_convolution_layer):
+    """
+    This function gets the final layer of the model.
+
+    Args:
+        final_convolution_layer (str): The final layer of the model as a string.
+
+    Returns:
+        Functional: sigmoid, softmax, or None
+    """
+    none_list = [
+        "none",
+        None,
+        "None",
+        "regression",
+        "classification_but_not_softmax",
+        "logits",
+        "classification_without_softmax",
+    ]
+
+    if final_convolution_layer in ["sigmoid", "sig"]:
+        final_convolution_layer = torch.sigmoid
+
+    elif final_convolution_layer in ["softmax", "soft"]:
+        final_convolution_layer = F.softmax
+
+    elif final_convolution_layer in none_list:
+        final_convolution_layer = None
+
+    return final_convolution_layer
diff --git a/GANDLF/utils/parameter_processing.py b/GANDLF/utils/parameter_processing.py
--- a/GANDLF/utils/parameter_processing.py
+++ b/GANDLF/utils/parameter_processing.py
@@ -1,4 +1,4 @@
-from GANDLF.models.modelBase import ModelBase
+from GANDLF.utils.modelbase import get_modelbase_final_layer
 
 
 def populate_header_in_parameters(parameters, headers):
@@ -38,7 +38,7 @@ def populate_header_in_parameters(parameters, headers):
         parameters["model"]["num_channels"] = len(headers["channelHeaders"])
 
     parameters["problem_type"] = find_problem_type(
-        parameters, ModelBase(parameters).final_convolution_layer
+        parameters, get_modelbase_final_layer(parameters["model"]["final_layer"])
     )
 
     # if the problem type is classification/segmentation, ensure the number of classes are picked from the configuration


Test patch: