kasn 0.1.0__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.
kasn/__init__.py ADDED
@@ -0,0 +1,49 @@
1
+ """Kolmogorov-Arnold Sieve Networks (KASN).
2
+
3
+ Sieve extremum estimation with deep Kolmogorov-Arnold networks, learned
4
+ B-spline edges and Group Lasso regularisation.
5
+
6
+ KASNRegressor
7
+ Squared, Huber, quantile and Poisson criteria.
8
+ KASNClassifier
9
+ Binary logistic criterion.
10
+ tune_kasn
11
+ Prune-aware greedy hyperparameter search on a validation fold.
12
+ make_kasn_regression
13
+ Monte Carlo designs of Section 4.
14
+ load
15
+ Restore any estimator written by ``.save()``.
16
+ """
17
+
18
+ from typing import Optional
19
+
20
+ from .base import _read_payload
21
+ from .classifier import KASNClassifier
22
+ from .datasets import make_kasn_regression, target_function
23
+ from .losses import make_loss
24
+ from .network import KASNLayer, KASNNetwork
25
+ from .preprocessing import EmpiricalCDFTransformer
26
+ from .regressor import KASNRegressor
27
+ from .splines import BSplineBasis
28
+ from .theory import architecture, check_conditions
29
+ from .tuning import TuningResult, tune_kasn
30
+
31
+ __version__ = "0.1.0"
32
+
33
+
34
+ def load(path, device: Optional[str] = None):
35
+ """Load a ``KASNRegressor`` or ``KASNClassifier`` written by ``.save()``."""
36
+ payload = _read_payload(path)
37
+ classes = {"KASNRegressor": KASNRegressor,
38
+ "KASNClassifier": KASNClassifier}
39
+ if payload["class"] not in classes:
40
+ raise ValueError(f"unknown estimator class {payload['class']!r}")
41
+ return classes[payload["class"]]._from_payload(payload, device)
42
+
43
+
44
+ __all__ = [
45
+ "KASNRegressor", "KASNClassifier", "load", "tune_kasn", "TuningResult",
46
+ "make_kasn_regression", "target_function", "make_loss",
47
+ "EmpiricalCDFTransformer", "BSplineBasis", "KASNLayer", "KASNNetwork",
48
+ "architecture", "check_conditions", "__version__",
49
+ ]
kasn/_utils.py ADDED
@@ -0,0 +1,55 @@
1
+ """Small internal helpers: devices, seeding and serialisation hygiene."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ from typing import Any, Iterator, Optional, Union
7
+
8
+ import numpy as np
9
+ import torch
10
+
11
+
12
+ def resolve_device(device: Union[str, torch.device, None]) -> torch.device:
13
+ """``'auto'`` (or ``None``) picks CUDA when available, otherwise CPU."""
14
+ if device is None or device == "auto":
15
+ return torch.device("cuda" if torch.cuda.is_available() else "cpu")
16
+ return torch.device(device)
17
+
18
+
19
+ @contextlib.contextmanager
20
+ def seeded(random_state: Optional[int],
21
+ device: torch.device) -> Iterator[None]:
22
+ """Seed torch inside a forked RNG so the caller's global state is untouched.
23
+
24
+ Every candidate in a hyperparameter search therefore starts from the same
25
+ initialisation, and repeated fits do not consume each other's draws.
26
+ """
27
+ if random_state is None:
28
+ yield
29
+ return
30
+ devices = []
31
+ if device.type == "cuda":
32
+ devices = [device.index if device.index is not None
33
+ else torch.cuda.current_device()]
34
+ with torch.random.fork_rng(devices=devices):
35
+ torch.manual_seed(int(random_state))
36
+ yield
37
+
38
+
39
+ def plain(obj: Any) -> Any:
40
+ """Recursively convert numpy scalars and arrays to built-in Python types.
41
+
42
+ ``torch.load(weights_only=True)`` accepts only built-in containers and
43
+ tensors, so everything saved alongside the state dict passes through here.
44
+ """
45
+ if isinstance(obj, dict):
46
+ return {str(k): plain(v) for k, v in obj.items()}
47
+ if isinstance(obj, (list, tuple)):
48
+ return type(obj)(plain(v) for v in obj)
49
+ if isinstance(obj, np.generic):
50
+ return obj.item()
51
+ if isinstance(obj, np.ndarray):
52
+ return obj.tolist()
53
+ if isinstance(obj, torch.device):
54
+ return str(obj)
55
+ return obj