aether-ml 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. aether/__init__.py +44 -0
  2. aether/__init__.pyi +69 -0
  3. aether/_utils/__init__.py +12 -0
  4. aether/_utils/__init__.pyi +14 -0
  5. aether/_utils/_lazy.py +52 -0
  6. aether/_utils/null_objects.py +91 -0
  7. aether/_utils/progress.py +596 -0
  8. aether/base.py +83 -0
  9. aether/config.py +257 -0
  10. aether/custom_kernels/__init__.py +0 -0
  11. aether/custom_kernels/adam_kernel.py +121 -0
  12. aether/custom_kernels/batchnorm_kernel.py +186 -0
  13. aether/custom_kernels/conv_kernel.py +932 -0
  14. aether/custom_kernels/dropout_kernel.py +118 -0
  15. aether/custom_kernels/launch_math.py +40 -0
  16. aether/custom_kernels/loss_kernel.py +34 -0
  17. aether/custom_kernels/pooling_kernel.py +403 -0
  18. aether/layers/__init__.py +18 -0
  19. aether/layers/__init__.pyi +21 -0
  20. aether/layers/activations.py +105 -0
  21. aether/layers/conv.py +385 -0
  22. aether/layers/dropout.py +186 -0
  23. aether/layers/linear.py +161 -0
  24. aether/layers/normalization.py +338 -0
  25. aether/layers/pooling.py +580 -0
  26. aether/losses/__init__.py +8 -0
  27. aether/losses/__init__.pyi +10 -0
  28. aether/losses/categorical_crossentropy.py +229 -0
  29. aether/metrics/__init__.py +9 -0
  30. aether/metrics/__init__.pyi +8 -0
  31. aether/metrics/accuracy.py +61 -0
  32. aether/model.py +989 -0
  33. aether/optimizers/__init__.py +9 -0
  34. aether/optimizers/__init__.pyi +8 -0
  35. aether/optimizers/adam.py +289 -0
  36. aether/preprocessing/__init__.py +13 -0
  37. aether/preprocessing/__init__.pyi +16 -0
  38. aether/preprocessing/_utils.py +73 -0
  39. aether/preprocessing/transforms.py +564 -0
  40. aether/py.typed +0 -0
  41. aether_ml-0.1.1.dist-info/METADATA +307 -0
  42. aether_ml-0.1.1.dist-info/RECORD +45 -0
  43. aether_ml-0.1.1.dist-info/WHEEL +5 -0
  44. aether_ml-0.1.1.dist-info/licenses/LICENSE +21 -0
  45. aether_ml-0.1.1.dist-info/top_level.txt +1 -0
aether/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ from importlib.metadata import PackageNotFoundError, version as _dist_version
2
+
3
+ from aether._utils._lazy import install_lazy_attrs
4
+ from aether.layers import _LAYER_MODULES
5
+ from aether.losses import _LOSS_MODULES
6
+ from aether.metrics import _ACC_MODULES
7
+ from aether.optimizers import _OPTIMIZER_MODULES
8
+ from aether.preprocessing import _PREPROCESSING_MODULES
9
+
10
+ try:
11
+ __version__ = _dist_version("aether-ml")
12
+ except PackageNotFoundError:
13
+ # Running from a source checkout that was never pip-installed. Keep the
14
+ # attribute defined so `ae.__version__` never falls through to the lazy
15
+ # __getattr__ and raises a misleading AttributeError.
16
+ __version__ = "0.0.0.dev0"
17
+
18
+ _MODEL_MODULES = {
19
+ "Model": "aether.model",
20
+ }
21
+ _TOP_LEVEL_MODULES = {
22
+ **_MODEL_MODULES,
23
+ **_LAYER_MODULES,
24
+ **_LOSS_MODULES,
25
+ **_ACC_MODULES,
26
+ **_OPTIMIZER_MODULES,
27
+ **_PREPROCESSING_MODULES
28
+ }
29
+
30
+ install_lazy_attrs(globals(), _TOP_LEVEL_MODULES)
31
+
32
+ # Clean up private setup variables so ae.<tab> remains clean
33
+ del (
34
+ install_lazy_attrs,
35
+ _dist_version,
36
+ PackageNotFoundError,
37
+ _LAYER_MODULES,
38
+ _LOSS_MODULES,
39
+ _ACC_MODULES,
40
+ _OPTIMIZER_MODULES,
41
+ _MODEL_MODULES,
42
+ _PREPROCESSING_MODULES,
43
+ _TOP_LEVEL_MODULES,
44
+ )
aether/__init__.pyi ADDED
@@ -0,0 +1,69 @@
1
+ # aether/__init__.pyi
2
+
3
+ # Static-typing companion to aether/__init__.py.
4
+ # Never executed -- exists purely so each public class
5
+ # gets real autocomplete/type info despite the runtime
6
+ # __getattr__ lazy loading.
7
+
8
+ from aether.model import Model as Model
9
+
10
+ from aether.layers.linear import Dense as Dense, Flatten as Flatten
11
+ from aether.layers.conv import Conv2d as Conv2d
12
+ from aether.layers.activations import ReLU as ReLU, LeakyReLU as LeakyReLU, SoftMax as SoftMax
13
+ from aether.layers.pooling import MaxPool2d as MaxPool2d, AvgPool2d as AvgPool2d, GlobalAvgPool as GlobalAvgPool
14
+ from aether.layers.dropout import Dropout as Dropout, SpatialDropout as SpatialDropout
15
+ from aether.layers.normalization import BatchNorm as BatchNorm
16
+ from aether.losses.categorical_crossentropy import (
17
+ Loss as Loss,
18
+ CategoricalCrossEntropy as CategoricalCrossEntropy,
19
+ SoftmaxCategoricalCrossEntropy as SoftmaxCategoricalCrossEntropy
20
+ )
21
+
22
+ # Direct module imports resolve stub lookups instantly:
23
+ from aether.metrics.accuracy import (
24
+ CategoricalAccuracy as CategoricalAccuracy,
25
+ RegressionAccuracy as RegressionAccuracy,
26
+ )
27
+
28
+ from aether.optimizers.adam import Adam as Adam, AdamW as AdamW
29
+
30
+ from aether.preprocessing.transforms import (
31
+ to_tensor as to_tensor,
32
+ ToTensor as ToTensor,
33
+ StandardScaler as StandardScaler,
34
+ Rescale as Rescale,
35
+ Compose as Compose
36
+ )
37
+ __all__ = [
38
+ # model
39
+ "Model",
40
+ # layers
41
+ "Dense",
42
+ "Flatten",
43
+ "Conv2d",
44
+ "ReLU",
45
+ "LeakyReLU",
46
+ "SoftMax",
47
+ "MaxPool2d",
48
+ "AvgPool2d",
49
+ "GlobalAvgPool",
50
+ "Dropout",
51
+ "SpatialDropout",
52
+ "BatchNorm",
53
+ # losses
54
+ "Loss",
55
+ "CategoricalCrossEntropy",
56
+ "SoftmaxCategoricalCrossEntropy",
57
+ # metrics
58
+ "CategoricalAccuracy",
59
+ "RegressionAccuracy",
60
+ # optimizers
61
+ "Adam",
62
+ "AdamW",
63
+ # preprocessing
64
+ "to_tensor",
65
+ "ToTensor",
66
+ "StandardScaler",
67
+ "Rescale",
68
+ "Compose"
69
+ ]
@@ -0,0 +1,12 @@
1
+ """Internal framework utilities. Never exposed outside aether"""
2
+
3
+ from aether._utils._lazy import install_lazy_attrs
4
+ from aether._utils.null_objects import NullAccuracy, NullOptimizer, NullPreprocessor
5
+ from aether._utils.progress import TrainingProgress
6
+ __all__ = [
7
+ "install_lazy_attrs",
8
+ "NullAccuracy",
9
+ "NullOptimizer",
10
+ "NullPreprocessor",
11
+ "TrainingProgress",
12
+ ]
@@ -0,0 +1,14 @@
1
+ from aether._utils._lazy import install_lazy_attrs as install_lazy_attrs
2
+ from aether._utils.null_objects import (
3
+ NullAccuracy as NullAccuracy,
4
+ NullOptimizer as NullOptimizer,
5
+ NullPreprocessor as NullPreprocessor
6
+ )
7
+ from aether._utils.progress import TrainingProgress as TrainingProgress
8
+ __all__ = [
9
+ "install_lazy_attrs",
10
+ "NullAccuracy",
11
+ "NullOptimizer",
12
+ "NullPreprocessor",
13
+ "TrainingProgress",
14
+ ]
aether/_utils/_lazy.py ADDED
@@ -0,0 +1,52 @@
1
+ import importlib
2
+
3
+ def install_lazy_attrs(module_globals, name_map):
4
+ """
5
+ Wire up a lazy __getattr__/__dir__ pair into the CALLING package's
6
+ module namespace.
7
+
8
+ Parameters
9
+ ----------
10
+ module_globals : dict
11
+ Must be the literal `globals()` of the __init__.py calling this
12
+ function. install_lazy_attrs writes __getattr__/__dir__/__all__
13
+ directly into that dict, which is what actually makes them live
14
+ on that specific package -- __getattr__ is resolved per-module,
15
+ not inherited up the package hierarchy, so every __init__.py that
16
+ wants lazy attribute resolution needs to call this itself.
17
+ name_map : dict[str, str]
18
+ {public_name: "fully.qualified.module.path"}. On first access of
19
+ `package.public_name`, the target module is imported and the
20
+ attribute is pulled off of it.
21
+
22
+ Notes
23
+ -----
24
+ The resolved value is cached directly into module_globals on first
25
+ access (module_globals[name] = value), so __getattr__ is only ever
26
+ invoked once per name -- every subsequent access is a plain,
27
+ zero-overhead attribute lookup, same as if it had been imported
28
+ eagerly. Also, While Python 3.15 PEP 810 - Explicit Lazy Imports
29
+ circumvents the need for a file like this, its no need for users
30
+ to be locked out of this package for not being on python >=3.15
31
+ """
32
+ module_name = module_globals.get("__name__", "<unknown>")
33
+
34
+ # Merge rather than clobber: a package may want a handful of names
35
+ # eagerly imported above the install_lazy_attrs() call (e.g. a
36
+ # version string) in addition to the lazy ones.
37
+ existing_all = set(module_globals.get("__all__", ()))
38
+ module_globals["__all__"] = sorted(existing_all | set(name_map))
39
+
40
+ def __getattr__(name):
41
+ if name in name_map:
42
+ target_module = importlib.import_module(name_map[name])
43
+ value = getattr(target_module, name)
44
+ module_globals[name] = value # cache: next access is a plain lookup
45
+ return value
46
+ raise AttributeError(f"module {module_name!r} has no attribute {name!r}")
47
+
48
+ def __dir__():
49
+ return sorted(set(module_globals.keys()) | set(name_map))
50
+
51
+ module_globals["__getattr__"] = __getattr__
52
+ module_globals["__dir__"] = __dir__
@@ -0,0 +1,91 @@
1
+ """
2
+ Simple null objects that are used by the Model class.
3
+ Avoids making us write needless branch conditionals.
4
+ """
5
+ from aether.preprocessing.transforms import Preprocess
6
+
7
+ class NullAccuracy:
8
+ def calculate(self, *args, **kwargs):
9
+ return 0.0
10
+
11
+ def calculate_accumulated(self):
12
+ return 0.0
13
+
14
+ def new_pass(self):
15
+ pass
16
+
17
+
18
+ class NullOptimizer:
19
+ def step(self):
20
+ pass
21
+
22
+ @property
23
+ def current_learning_rate(self):
24
+ return None
25
+
26
+ class _NullProgress:
27
+ """
28
+ No-op stand-in for ``TrainingProgress`` used when ``verbose == 0``.
29
+ """
30
+
31
+ __slots__ = ()
32
+
33
+ def start_epoch(self, epoch: int) -> None:
34
+ return
35
+
36
+ def tick(self, step: int, force: bool = False) -> None:
37
+ return
38
+
39
+ def update_metrics(
40
+ self,
41
+ step: int,
42
+ loss: float,
43
+ acc: float,
44
+ lr: float,
45
+ reg_loss: float | None = None,
46
+ ) -> None:
47
+ return
48
+
49
+ def commit_epoch(
50
+ self,
51
+ epoch: int,
52
+ epoch_loss: float,
53
+ epoch_acc: float,
54
+ lr: float,
55
+ ) -> None:
56
+ return
57
+
58
+ def commit_validation(self, val_loss: float, val_acc: float) -> None:
59
+ return
60
+
61
+ def close(self) -> None:
62
+ return
63
+
64
+ class NullPreprocessor(Preprocess):
65
+ """Null-object preprocessor for models with no attached preprocessing pipeline.
66
+
67
+ A pure identity: ``transform()`` hands back exactly what it was given, and the
68
+ device/precision hooks are genuine no-ops. Model.finalize() installs this
69
+ unconditionally so the per-batch dispatch can call ``.transform()`` with zero
70
+ ``is not None`` branching in the hot loop -- the same role NullOptimizer and
71
+ NullAccuracy play elsewhere in the codebase.
72
+
73
+ Deliberately does NOT absorb device migration or precision casting. Every
74
+ finalized model owns one of these, so a migrating null object would silently
75
+ add a host->device copy to every batch of every pipeline-free model -- exactly
76
+ the per-step PCIe traffic that Model._assert_device_alignment exists to forbid.
77
+ Device strictness stays with that guard; device migration stays with an
78
+ explicit ToTensor the user opted into.
79
+
80
+ Trivially fit, so it never triggers the implicit-fit path in train().
81
+ """
82
+ is_fitted = True
83
+
84
+ def _compile_for_device(self, device):
85
+ return
86
+
87
+ def _apply_precision(self, policy):
88
+ return
89
+
90
+ def transform(self, X):
91
+ return X