From 766a7d1adf038f893e35dcf82d497d357521017b Mon Sep 17 00:00:00 2001 From: Abe Levitan Date: Mon, 13 Oct 2025 14:20:38 +0200 Subject: [PATCH] Move to better supported strategy for avoiding circular imports --- src/cdtools/models/base.py | 17 +---------------- src/cdtools/reconstructors/adam.py | 9 +++++++-- src/cdtools/reconstructors/base.py | 9 +++++++-- src/cdtools/reconstructors/lbfgs.py | 10 ++++++++-- src/cdtools/reconstructors/sgd.py | 10 ++++++++-- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/cdtools/models/base.py b/src/cdtools/models/base.py index 635cdfb..5d3ac9a 100644 --- a/src/cdtools/models/base.py +++ b/src/cdtools/models/base.py @@ -40,6 +40,7 @@ import time from scipy import io from contextlib import contextmanager from cdtools.tools.data import nested_dict_to_h5, h5_to_nested_dict, nested_dict_to_numpy, nested_dict_to_torch +from cdtools.reconstructors import AdamReconstructor, LBFGSReconstructor, SGDReconstructor from cdtools.datasets import CDataset from typing import List, Union, Tuple import os @@ -378,13 +379,7 @@ class CDIModel(t.nn.Module): # cdtools.reconstructors.AdamReconstructor to perform reconstructions # without creating a new reconstructor each time we update the # hyperparameters. - # - # The only way to do this is to make the Adam reconstructor an - # attribute of the model. But since the Adam reconstructor also - # depends on CDIModel, this seems to give rise to a circular import - # error unless we import cdtools.reconstructors within this method: if not hasattr(self, 'reconstructor'): - from cdtools.reconstructors import AdamReconstructor self.reconstructor = AdamReconstructor(model=self, dataset=dataset, subset=subset) @@ -449,12 +444,7 @@ class CDIModel(t.nn.Module): # cdtools.reconstructors.LBFGSReconstructor as an attribute to run # reconstructions without generating new reconstructors each time # CDIModel.LBFGS_optimize is called. - # - # Since the LBFGS reconstructor also depends on CDIModel, a circular - # import error arises unless we import cdtools.reconstructors within - # this method: if not hasattr(self, 'reconstructor'): - from cdtools.reconstructors import LBFGSReconstructor self.reconstructor = LBFGSReconstructor(model=self, dataset=dataset, subset=subset) @@ -524,12 +514,7 @@ class CDIModel(t.nn.Module): # cdtools.reconstructors.SGDReconstructor as an attribute to run # reconstructions without generating new reconstructors each time # CDIModel.SGD_optimize is called. - # - # Since the SGD reconstructor also depends on CDIModel, a circular - # import error arises unless we import cdtools.reconstructors within - # this method: if not hasattr(self, 'reconstructor'): - from cdtools.reconstructors import SGDReconstructor self.reconstructor = SGDReconstructor(model=self, dataset=dataset, subset=subset) diff --git a/src/cdtools/reconstructors/adam.py b/src/cdtools/reconstructors/adam.py index 6eecc9c..4c54f1f 100644 --- a/src/cdtools/reconstructors/adam.py +++ b/src/cdtools/reconstructors/adam.py @@ -6,12 +6,17 @@ The Reconstructor class is designed to resemble so-called 'Trainer' classes that (in the language of the AI/ML folks) handles the 'training' of a model given some dataset and optimizer. """ +from __future__ import annotations +from typing import TYPE_CHECKING + import torch as t -from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset -from cdtools.models import CDIModel from typing import Tuple, List, Union from cdtools.reconstructors import Reconstructor +if TYPE_CHECKING: + from cdtools.models import CDIModel + from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset + __all__ = ['AdamReconstructor'] diff --git a/src/cdtools/reconstructors/base.py b/src/cdtools/reconstructors/base.py index fd2cbb2..1937661 100644 --- a/src/cdtools/reconstructors/base.py +++ b/src/cdtools/reconstructors/base.py @@ -8,16 +8,21 @@ the 'training' of a model given some dataset and optimizer. The subclasses of Reconstructor are required to implement their own data loaders and optimizer adjusters """ +from __future__ import annotations +from typing import TYPE_CHECKING import torch as t from torch.utils import data as td import threading import queue import time -from cdtools.datasets import CDataset -from cdtools.models import CDIModel from typing import List, Union +if TYPE_CHECKING: + from cdtools.models import CDIModel + from cdtools.datasets import CDataset + + __all__ = ['Reconstructor'] diff --git a/src/cdtools/reconstructors/lbfgs.py b/src/cdtools/reconstructors/lbfgs.py index 8bbd7ae..60c6f97 100644 --- a/src/cdtools/reconstructors/lbfgs.py +++ b/src/cdtools/reconstructors/lbfgs.py @@ -6,12 +6,18 @@ The Reconstructor class is designed to resemble so-called 'Trainer' classes that (in the language of the AI/ML folks) handles the 'training' of a model given some dataset and optimizer. """ +from __future__ import annotations +from typing import TYPE_CHECKING + import torch as t -from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset -from cdtools.models import CDIModel from typing import List, Union from cdtools.reconstructors import Reconstructor +if TYPE_CHECKING: + from cdtools.models import CDIModel + from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset + + __all__ = ['LBFGSReconstructor'] diff --git a/src/cdtools/reconstructors/sgd.py b/src/cdtools/reconstructors/sgd.py index a8f2ebc..5b7bf4e 100644 --- a/src/cdtools/reconstructors/sgd.py +++ b/src/cdtools/reconstructors/sgd.py @@ -6,12 +6,18 @@ The Reconstructor class is designed to resemble so-called 'Trainer' classes that (in the language of the AI/ML folks) handles the 'training' of a model given some dataset and optimizer. """ +from __future__ import annotations +from typing import TYPE_CHECKING + import torch as t -from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset -from cdtools.models import CDIModel from typing import List, Union from cdtools.reconstructors import Reconstructor +if TYPE_CHECKING: + from cdtools.models import CDIModel + from cdtools.datasets.ptycho_2d_dataset import Ptycho2DDataset + + __all__ = ['SGDReconstructor']