Lazy access to the optional PyTorch backend.
A handful of TuiML algorithms — the attention-based tabular models and the deep forecasters — need a tensor library with autograd. PyTorch is therefore an optional dependency, installed with pip install 'tuiml[torch]'.
"Optional" is enforced at three levels, and all three matter:
-
Import time. Importing
tuiml, or any package containing a
list_algorithms() and the docs show the same catalog on every install.
-
Construction time.
FTTransformerClassifier()succeeds without torch.
-
Fit time.
require_torchis called fromfit, which is the
ImportError naming the class and the exact install command.
The result is that a user without torch sees the model in the catalog, can inspect its parameters, and gets one clear, actionable message the moment they try to train it — rather than a bare ModuleNotFoundError from deep inside a layer definition.
>>> from tuiml.utils.torch_backend import has_torch, require_torch
>>> isinstance(has_torch(), bool)
True
Functions
Report whether PyTorch is importable.
require_torch instead, so the user gets an actionable message rather than a silent fallback.Returns
available
True if import torch succeeds.
>>> from tuiml.utils.torch_backend import has_torch
>>> isinstance(has_torch(), bool)
True
Import torch, or raise an error naming the class and the install command.
fit. It is deliberately not called from __init__: constructing a model records hyperparameters and must keep working on an install without torch, so that parameter grids, pickling and the generic algorithm contract behave identically everywhere.Parameters
cls_name
Returns
torch
torch module.
nn
torch.nn, returned alongside because every caller needs both.
Raises
ImportError
>>> from tuiml.utils.torch_backend import has_torch, require_torch
>>> if has_torch():
... torch, nn = require_torch("MyModel")
... print(hasattr(nn, "Linear"))
... else:
... print(True)
True
Force torch to one thread when a second OpenMP runtime is loaded.
On macOS, xgboost, LightGBM and CatBoost each load a bundled libomp.dylib, and algorithms imports all three. Torch is then imported afterwards -- which is exactly what the optional-dependency contract demands -- leaving two OpenMP runtimes in one process. The first torch operation to open a parallel region segfaults the interpreter, with no Python traceback.
Running torch single-threaded avoids parallel regions entirely. Note that the usual advice, KMP_DUPLICATE_LIB_OK=TRUE, does not help here: it still segfaults. Only clamping the thread count, or importing torch before the boosting libraries, avoids the crash.
The guard is a mitigation, not a cure -- it costs multi-threaded torch on macOS. The real fix is to stop importing three third-party boosting libraries unconditionally from a core namespace.
Does nothing when no conflicting library is loaded, so a torch-only process keeps all its threads.
Parameters
torch_module
torch module.
Returns
applied
Turn a device string into a torch.device, falling back to CPU.
"auto" prefers CUDA, then Apple Silicon's MPS, then CPU. An explicit request for an unavailable accelerator falls back to CPU rather than raising: the model still trains, only slower, and a hard failure here would make a saved parameter grid non-portable between machines.Parameters
device
torch_module
torch module, passed in so this function never imports it.
Returns
device