USN 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.
- usn/__init__.py +145 -0
- usn/__init__.pyi +39 -0
- usn/backends/__init__.py +25 -0
- usn/backends/acceleration.py +236 -0
- usn/backends/detection.py +92 -0
- usn/backends/fallbacks.py +102 -0
- usn/backends/triton_kernels.py +421 -0
- usn/cli/__init__.py +5 -0
- usn/cli/main.py +212 -0
- usn/config/__init__.py +11 -0
- usn/config/generation_config.py +53 -0
- usn/config/model_config.py +296 -0
- usn/config/training_config.py +106 -0
- usn/core/__init__.py +31 -0
- usn/core/activations.py +94 -0
- usn/core/base.py +52 -0
- usn/core/interfaces.py +94 -0
- usn/core/types.py +83 -0
- usn/datasets/__init__.py +7 -0
- usn/datasets/collate.py +48 -0
- usn/datasets/math_dataset.py +102 -0
- usn/datasets/usn_dataset.py +145 -0
- usn/exceptions.py +131 -0
- usn/inference/__init__.py +5 -0
- usn/inference/generator.py +587 -0
- usn/layers/__init__.py +28 -0
- usn/layers/block.py +224 -0
- usn/layers/chunked_scan.py +133 -0
- usn/layers/norm.py +98 -0
- usn/layers/parallel_scan.py +147 -0
- usn/losses/__init__.py +5 -0
- usn/losses/cross_entropy.py +72 -0
- usn/models/__init__.py +87 -0
- usn/models/embedding.py +124 -0
- usn/models/usn_model.py +253 -0
- usn/modules/__init__.py +31 -0
- usn/modules/channel_mixing.py +84 -0
- usn/modules/exponential_gating.py +111 -0
- usn/modules/input_projection.py +66 -0
- usn/modules/selective_writing.py +146 -0
- usn/modules/state_readout.py +107 -0
- usn/modules/state_update.py +211 -0
- usn/modules/temporal_mixing.py +100 -0
- usn/optim/__init__.py +21 -0
- usn/optim/factory.py +154 -0
- usn/optim/schedulers.py +254 -0
- usn/py.typed +0 -0
- usn/serialization/__init__.py +33 -0
- usn/serialization/export.py +126 -0
- usn/serialization/format_spec.py +129 -0
- usn/serialization/migration.py +145 -0
- usn/serialization/reader.py +461 -0
- usn/serialization/validator.py +187 -0
- usn/serialization/writer.py +405 -0
- usn/tokenizers/__init__.py +14 -0
- usn/tokenizers/bpe_tokenizer.py +241 -0
- usn/tokenizers/char_tokenizer.py +150 -0
- usn/tokenizers/word_tokenizer.py +158 -0
- usn/training/__init__.py +7 -0
- usn/training/curriculum.py +91 -0
- usn/training/distributed.py +99 -0
- usn/training/stability.py +113 -0
- usn/training/trainer.py +566 -0
- usn/utils/__init__.py +52 -0
- usn/utils/counting.py +206 -0
- usn/utils/diagnostics.py +373 -0
- usn/utils/profiling.py +249 -0
- usn/utils/seed.py +60 -0
- usn/utils/timing.py +144 -0
- usn/utils/visualization.py +207 -0
- usn-0.1.0.dist-info/METADATA +96 -0
- usn-0.1.0.dist-info/RECORD +76 -0
- usn-0.1.0.dist-info/WHEEL +5 -0
- usn-0.1.0.dist-info/entry_points.txt +2 -0
- usn-0.1.0.dist-info/licenses/LICENSE +21 -0
- usn-0.1.0.dist-info/top_level.txt +1 -0
usn/__init__.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""USN - Unified State Network Architecture Library.
|
|
2
|
+
|
|
3
|
+
A production-grade Python package implementing the Unified State Network (USN)
|
|
4
|
+
architecture for autoregressive sequence modeling with O(n) training complexity
|
|
5
|
+
via associative parallel scan and O(1) inference memory via constant-size state.
|
|
6
|
+
|
|
7
|
+
Quick Start:
|
|
8
|
+
>>> import usn
|
|
9
|
+
>>> model = usn.create_model("tiny")
|
|
10
|
+
>>> print(model.summary())
|
|
11
|
+
|
|
12
|
+
Author: BUEORM
|
|
13
|
+
License: MIT
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
__author__ = "BUEORM"
|
|
18
|
+
|
|
19
|
+
# Core classes
|
|
20
|
+
# Backends
|
|
21
|
+
from usn.backends import AccelerationLevel, AccelerationManager, DeviceDetector
|
|
22
|
+
from usn.config import USNConfig, USNGenerationConfig, USNTrainingConfig
|
|
23
|
+
from usn.inference import USNGenerator
|
|
24
|
+
from usn.models import create_model
|
|
25
|
+
from usn.models.usn_model import USNModel
|
|
26
|
+
from usn.serialization.export import export_model
|
|
27
|
+
from usn.serialization.reader import USNReader
|
|
28
|
+
|
|
29
|
+
# Serialization
|
|
30
|
+
from usn.serialization.writer import USNWriter
|
|
31
|
+
from usn.training import USNTrainer
|
|
32
|
+
from usn.utils.counting import count_parameters, estimate_flops, estimate_memory
|
|
33
|
+
|
|
34
|
+
# Utilities
|
|
35
|
+
from usn.utils.seed import set_seed
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# High-level API functions
|
|
39
|
+
def save(model, path: str, **kwargs) -> None:
|
|
40
|
+
"""Save a model to .usn format."""
|
|
41
|
+
writer = USNWriter()
|
|
42
|
+
config = getattr(model, "config", None)
|
|
43
|
+
writer.save(path, model, config=config, **kwargs)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load(path: str, map_location=None):
|
|
47
|
+
"""Load a model from .usn format."""
|
|
48
|
+
reader = USNReader()
|
|
49
|
+
data = reader.load(path, map_location=map_location)
|
|
50
|
+
config = data.get("config")
|
|
51
|
+
if config is None:
|
|
52
|
+
raise ValueError("No config found in .usn file")
|
|
53
|
+
model = USNModel(config)
|
|
54
|
+
weights = data.get("weights", {})
|
|
55
|
+
if weights:
|
|
56
|
+
# Filter buffer keys for state_dict loading
|
|
57
|
+
state_dict = {k: v for k, v in weights.items() if not k.startswith("__buffer__.")}
|
|
58
|
+
model.load_state_dict(state_dict, strict=False)
|
|
59
|
+
return model
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def export(model, format: str, path: str, **kwargs) -> None:
|
|
63
|
+
"""Export model to standard format (onnx, safetensors, state_dict, torchscript)."""
|
|
64
|
+
config = getattr(model, "config", None)
|
|
65
|
+
export_model(model, format, path, config=config, **kwargs)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def generate(model, prompt: str, max_tokens: int = 256, tokenizer=None, **kwargs) -> str:
|
|
69
|
+
"""Generate text from a model (convenience function)."""
|
|
70
|
+
if tokenizer is None:
|
|
71
|
+
raise ValueError("tokenizer is required for generate()")
|
|
72
|
+
gen = USNGenerator(model, tokenizer)
|
|
73
|
+
output = gen.generate(prompt, max_new_tokens=max_tokens, **kwargs)
|
|
74
|
+
return tokenizer.decode(output.token_ids[0].tolist())
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def train(model, dataset, config=None, **kwargs):
|
|
78
|
+
"""Train a model (convenience function)."""
|
|
79
|
+
if config is None:
|
|
80
|
+
config = USNTrainingConfig()
|
|
81
|
+
trainer = USNTrainer(model, dataset, config, **kwargs)
|
|
82
|
+
return trainer.train()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def summary(model) -> str:
|
|
86
|
+
"""Get model summary string."""
|
|
87
|
+
if hasattr(model, "summary"):
|
|
88
|
+
return model.summary()
|
|
89
|
+
return f"Model with {count_parameters(model):,} parameters"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def from_pretrained(path_or_id: str):
|
|
93
|
+
"""Load a pretrained model from path."""
|
|
94
|
+
return load(path_or_id)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def device_info() -> dict:
|
|
98
|
+
"""Get available device information."""
|
|
99
|
+
return DeviceDetector.detect()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def set_acceleration_level(level: int) -> None:
|
|
103
|
+
"""Set the acceleration level manually."""
|
|
104
|
+
AccelerationManager.set_level(level)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def benchmark_acceleration() -> dict:
|
|
108
|
+
"""Compare throughput across acceleration levels."""
|
|
109
|
+
return {"current_level": AccelerationManager.get_level().name}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
__all__ = [
|
|
113
|
+
# Version
|
|
114
|
+
"__version__",
|
|
115
|
+
"__author__",
|
|
116
|
+
# Core classes
|
|
117
|
+
"USNModel",
|
|
118
|
+
"USNConfig",
|
|
119
|
+
"USNTrainingConfig",
|
|
120
|
+
"USNGenerationConfig",
|
|
121
|
+
"USNTrainer",
|
|
122
|
+
"USNGenerator",
|
|
123
|
+
# Factory
|
|
124
|
+
"create_model",
|
|
125
|
+
# High-level API
|
|
126
|
+
"save",
|
|
127
|
+
"load",
|
|
128
|
+
"export",
|
|
129
|
+
"generate",
|
|
130
|
+
"train",
|
|
131
|
+
"summary",
|
|
132
|
+
"from_pretrained",
|
|
133
|
+
# Utilities
|
|
134
|
+
"set_seed",
|
|
135
|
+
"count_parameters",
|
|
136
|
+
"estimate_memory",
|
|
137
|
+
"estimate_flops",
|
|
138
|
+
"device_info",
|
|
139
|
+
"set_acceleration_level",
|
|
140
|
+
"benchmark_acceleration",
|
|
141
|
+
# Backend
|
|
142
|
+
"AccelerationLevel",
|
|
143
|
+
"AccelerationManager",
|
|
144
|
+
"DeviceDetector",
|
|
145
|
+
]
|
usn/__init__.pyi
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Type stubs for the USN public API."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import torch.nn as nn
|
|
6
|
+
from torch import Tensor
|
|
7
|
+
|
|
8
|
+
from usn.backends.acceleration import AccelerationLevel as AccelerationLevel
|
|
9
|
+
from usn.backends.acceleration import AccelerationManager as AccelerationManager
|
|
10
|
+
from usn.backends.detection import DeviceDetector as DeviceDetector
|
|
11
|
+
from usn.config.generation_config import USNGenerationConfig as USNGenerationConfig
|
|
12
|
+
from usn.config.model_config import USNConfig as USNConfig
|
|
13
|
+
from usn.config.training_config import USNTrainingConfig as USNTrainingConfig
|
|
14
|
+
from usn.inference.generator import USNGenerator as USNGenerator
|
|
15
|
+
from usn.models import create_model as create_model
|
|
16
|
+
from usn.models.usn_model import USNModel as USNModel
|
|
17
|
+
from usn.training.trainer import USNTrainer as USNTrainer
|
|
18
|
+
from usn.utils.counting import count_parameters as count_parameters
|
|
19
|
+
from usn.utils.counting import estimate_flops as estimate_flops
|
|
20
|
+
from usn.utils.counting import estimate_memory as estimate_memory
|
|
21
|
+
from usn.utils.seed import set_seed as set_seed
|
|
22
|
+
|
|
23
|
+
__version__: str
|
|
24
|
+
__author__: str
|
|
25
|
+
|
|
26
|
+
def save(model: nn.Module, path: str, **kwargs: Any) -> None: ...
|
|
27
|
+
def load(path: str, map_location: str | None = None) -> USNModel: ...
|
|
28
|
+
def export(model: nn.Module, format: str, path: str, **kwargs: Any) -> None: ...
|
|
29
|
+
def generate(
|
|
30
|
+
model: nn.Module, prompt: str, max_tokens: int = 256, tokenizer: Any = None, **kwargs: Any
|
|
31
|
+
) -> str: ...
|
|
32
|
+
def train(
|
|
33
|
+
model: nn.Module, dataset: Any, config: USNTrainingConfig | None = None, **kwargs: Any
|
|
34
|
+
) -> dict[str, Any]: ...
|
|
35
|
+
def summary(model: nn.Module) -> str: ...
|
|
36
|
+
def from_pretrained(path_or_id: str) -> USNModel: ...
|
|
37
|
+
def device_info() -> dict[str, Any]: ...
|
|
38
|
+
def set_acceleration_level(level: int) -> None: ...
|
|
39
|
+
def benchmark_acceleration() -> dict[str, Any]: ...
|
usn/backends/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Backend acceleration: device detection, Triton kernels, and fallbacks.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
- DeviceDetector: Hardware auto-detection (detect(), best_device())
|
|
5
|
+
- AccelerationLevel: Enum of 4 acceleration tiers
|
|
6
|
+
- AccelerationManager: Manages acceleration level with graceful fallback
|
|
7
|
+
|
|
8
|
+
Importing this package triggers kernel registration at all acceleration levels:
|
|
9
|
+
- triton_kernels: Registers Level 1 (TRITON), Level 3 (AUTOGRAD), Level 4 (EAGER)
|
|
10
|
+
- fallbacks: Registers Level 2 (COMPILE) via torch.compile wrappers
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Import fallbacks to register Level 2 (COMPILE) kernels
|
|
14
|
+
import usn.backends.fallbacks as _fallbacks # noqa: F401
|
|
15
|
+
|
|
16
|
+
# Import triton_kernels to register Level 1, 3, 4 kernels
|
|
17
|
+
import usn.backends.triton_kernels as _triton_kernels # noqa: F401
|
|
18
|
+
from usn.backends.acceleration import AccelerationLevel, AccelerationManager
|
|
19
|
+
from usn.backends.detection import DeviceDetector
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"AccelerationLevel",
|
|
23
|
+
"AccelerationManager",
|
|
24
|
+
"DeviceDetector",
|
|
25
|
+
]
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Acceleration level management for the USN library.
|
|
2
|
+
|
|
3
|
+
Implements a 4-level acceleration hierarchy with automatic detection and
|
|
4
|
+
graceful fallback. The levels are:
|
|
5
|
+
|
|
6
|
+
1. TRITON — Custom Triton fused kernels (fastest, requires triton + CUDA)
|
|
7
|
+
2. COMPILE — torch.compile with inductor backend (requires PyTorch 2.0+ CUDA)
|
|
8
|
+
3. AUTOGRAD — Custom autograd functions (requires CUDA)
|
|
9
|
+
4. EAGER — Standard PyTorch eager mode (always available, baseline)
|
|
10
|
+
|
|
11
|
+
The system auto-detects the best level at import time and NEVER fails due to
|
|
12
|
+
unavailable acceleration — it always falls back to EAGER as the last resort.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from enum import IntEnum
|
|
20
|
+
|
|
21
|
+
import torch
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AccelerationLevel(IntEnum):
|
|
27
|
+
"""4-level acceleration hierarchy for USN kernels.
|
|
28
|
+
|
|
29
|
+
Lower numeric value = faster / more specialized.
|
|
30
|
+
Higher numeric value = more compatible / always available.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
TRITON = 1
|
|
34
|
+
"""Custom Triton fused kernels — maximum performance."""
|
|
35
|
+
|
|
36
|
+
COMPILE = 2
|
|
37
|
+
"""torch.compile with inductor — good performance, no Triton needed."""
|
|
38
|
+
|
|
39
|
+
AUTOGRAD = 3
|
|
40
|
+
"""Custom autograd functions — CUDA required, no JIT compilation."""
|
|
41
|
+
|
|
42
|
+
EAGER = 4
|
|
43
|
+
"""Standard PyTorch eager mode — always available, baseline performance."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _detect_best_level() -> AccelerationLevel:
|
|
47
|
+
"""Detect the best available acceleration level.
|
|
48
|
+
|
|
49
|
+
Decision tree:
|
|
50
|
+
1. Is Triton installed AND CUDA available? → TRITON
|
|
51
|
+
2. Is torch.compile available (PyTorch ≥ 2.0) AND CUDA available? → COMPILE
|
|
52
|
+
3. Is CUDA available? → AUTOGRAD
|
|
53
|
+
4. Otherwise → EAGER
|
|
54
|
+
|
|
55
|
+
This function NEVER raises — it always returns a valid level.
|
|
56
|
+
"""
|
|
57
|
+
# Check Level 1: Triton
|
|
58
|
+
if torch.cuda.is_available():
|
|
59
|
+
try:
|
|
60
|
+
import triton # noqa: F401
|
|
61
|
+
import triton.language # noqa: F401
|
|
62
|
+
|
|
63
|
+
return AccelerationLevel.TRITON
|
|
64
|
+
except (ImportError, RuntimeError, OSError):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
# Check Level 2: torch.compile with inductor
|
|
68
|
+
if torch.cuda.is_available() and hasattr(torch, "compile"):
|
|
69
|
+
try:
|
|
70
|
+
|
|
71
|
+
@torch.compile(backend="inductor")
|
|
72
|
+
def _test_fn(x: torch.Tensor) -> torch.Tensor:
|
|
73
|
+
return x + 1
|
|
74
|
+
|
|
75
|
+
_test_fn(torch.zeros(1, device="cuda"))
|
|
76
|
+
return AccelerationLevel.COMPILE
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
# Check Level 3: Custom autograd (CUDA available but no compile)
|
|
81
|
+
if torch.cuda.is_available():
|
|
82
|
+
return AccelerationLevel.AUTOGRAD
|
|
83
|
+
|
|
84
|
+
# Level 4: Eager (CPU or unsupported hardware)
|
|
85
|
+
return AccelerationLevel.EAGER
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AccelerationManager:
|
|
89
|
+
"""Manages the 4-level acceleration hierarchy with graceful fallback.
|
|
90
|
+
|
|
91
|
+
This is a class-level (singleton-style) manager. All methods are classmethods
|
|
92
|
+
that operate on shared state. The acceleration level is auto-detected at
|
|
93
|
+
import time but can be overridden manually.
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
# Auto-detected at import
|
|
97
|
+
level = AccelerationManager.get_level()
|
|
98
|
+
|
|
99
|
+
# Manual override
|
|
100
|
+
AccelerationManager.set_level(AccelerationLevel.EAGER)
|
|
101
|
+
|
|
102
|
+
# Get kernel for current level
|
|
103
|
+
kernel = AccelerationManager.get_kernel("projections")
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
_current_level: AccelerationLevel = AccelerationLevel.EAGER
|
|
107
|
+
_detected_level: AccelerationLevel = AccelerationLevel.EAGER
|
|
108
|
+
_initialized: bool = False
|
|
109
|
+
_kernel_registry: dict[str, dict[AccelerationLevel, Callable]] = {}
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def _ensure_initialized(cls) -> None:
|
|
113
|
+
"""Lazily initialize by detecting the best level."""
|
|
114
|
+
if not cls._initialized:
|
|
115
|
+
cls._detected_level = _detect_best_level()
|
|
116
|
+
cls._current_level = cls._detected_level
|
|
117
|
+
cls._initialized = True
|
|
118
|
+
logger.info(
|
|
119
|
+
"USN acceleration level auto-detected: %s",
|
|
120
|
+
cls._current_level.name,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def detect_best_level(cls) -> AccelerationLevel:
|
|
125
|
+
"""Detect and return the best available acceleration level.
|
|
126
|
+
|
|
127
|
+
This re-runs detection (useful if hardware state has changed).
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
The best AccelerationLevel available on this system.
|
|
131
|
+
"""
|
|
132
|
+
cls._detected_level = _detect_best_level()
|
|
133
|
+
cls._current_level = cls._detected_level
|
|
134
|
+
cls._initialized = True
|
|
135
|
+
return cls._detected_level
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def set_level(cls, level: int | AccelerationLevel) -> None:
|
|
139
|
+
"""Manually set the acceleration level.
|
|
140
|
+
|
|
141
|
+
Use this to override auto-detection, e.g., for debugging or
|
|
142
|
+
benchmarking different backends.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
level: The desired acceleration level (int 1-4 or AccelerationLevel).
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ValueError: If level is not a valid AccelerationLevel value.
|
|
149
|
+
"""
|
|
150
|
+
cls._ensure_initialized()
|
|
151
|
+
try:
|
|
152
|
+
cls._current_level = AccelerationLevel(level)
|
|
153
|
+
except ValueError:
|
|
154
|
+
valid = [f"{l.name}={l.value}" for l in AccelerationLevel]
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"Invalid acceleration level: {level!r}. Valid levels: {', '.join(valid)}"
|
|
157
|
+
) from None
|
|
158
|
+
logger.info("USN acceleration level set to: %s", cls._current_level.name)
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def get_level(cls) -> AccelerationLevel:
|
|
162
|
+
"""Get the current acceleration level.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The currently active AccelerationLevel.
|
|
166
|
+
"""
|
|
167
|
+
cls._ensure_initialized()
|
|
168
|
+
return cls._current_level
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def get_kernel(cls, kernel_name: str) -> Callable:
|
|
172
|
+
"""Get the appropriate kernel implementation for the current level.
|
|
173
|
+
|
|
174
|
+
Looks up the kernel in the registry for the current acceleration level.
|
|
175
|
+
If the kernel is not available at the current level, falls back to the
|
|
176
|
+
next available level (higher number = more compatible).
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
kernel_name: Name of the kernel to retrieve (e.g., "projections",
|
|
180
|
+
"temporal_gate", "state_core", "channel_mlp").
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Callable implementing the requested kernel at the best available level.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
KeyError: If kernel_name is not registered at any level.
|
|
187
|
+
"""
|
|
188
|
+
cls._ensure_initialized()
|
|
189
|
+
|
|
190
|
+
if kernel_name not in cls._kernel_registry:
|
|
191
|
+
raise KeyError(
|
|
192
|
+
f"Unknown kernel '{kernel_name}'. "
|
|
193
|
+
f"Registered kernels: {list(cls._kernel_registry.keys())}"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
level_map = cls._kernel_registry[kernel_name]
|
|
197
|
+
|
|
198
|
+
# Try current level, then fall back to higher (more compatible) levels
|
|
199
|
+
for level in sorted(AccelerationLevel, key=lambda l: l.value):
|
|
200
|
+
if level.value >= cls._current_level.value and level in level_map:
|
|
201
|
+
return level_map[level]
|
|
202
|
+
|
|
203
|
+
# Should not reach here if EAGER is always registered
|
|
204
|
+
raise KeyError(
|
|
205
|
+
f"No implementation found for kernel '{kernel_name}' "
|
|
206
|
+
f"at level {cls._current_level.name} or below."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def register_kernel(
|
|
211
|
+
cls,
|
|
212
|
+
kernel_name: str,
|
|
213
|
+
level: AccelerationLevel,
|
|
214
|
+
implementation: Callable,
|
|
215
|
+
) -> None:
|
|
216
|
+
"""Register a kernel implementation at a specific acceleration level.
|
|
217
|
+
|
|
218
|
+
This is used by the backend modules (triton_kernels, fallbacks) to
|
|
219
|
+
populate the registry.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
kernel_name: Name of the kernel (e.g., "projections").
|
|
223
|
+
level: The acceleration level this implementation targets.
|
|
224
|
+
implementation: The callable implementing the kernel.
|
|
225
|
+
"""
|
|
226
|
+
if kernel_name not in cls._kernel_registry:
|
|
227
|
+
cls._kernel_registry[kernel_name] = {}
|
|
228
|
+
cls._kernel_registry[kernel_name][level] = implementation
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def reset(cls) -> None:
|
|
232
|
+
"""Reset manager to uninitialized state. Useful for testing."""
|
|
233
|
+
cls._current_level = AccelerationLevel.EAGER
|
|
234
|
+
cls._detected_level = AccelerationLevel.EAGER
|
|
235
|
+
cls._initialized = False
|
|
236
|
+
cls._kernel_registry = {}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Hardware device detection for the USN library.
|
|
2
|
+
|
|
3
|
+
Provides automatic detection of available compute hardware (CUDA GPUs, Apple MPS,
|
|
4
|
+
CPU) and selection of the optimal device for model execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import torch
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DeviceDetector:
|
|
15
|
+
"""Auto-detect available hardware and capabilities.
|
|
16
|
+
|
|
17
|
+
Provides static methods for querying the runtime environment's compute
|
|
18
|
+
resources. Used by AccelerationManager to determine the best acceleration
|
|
19
|
+
level and by model initialization to select the target device.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def detect() -> dict[str, Any]:
|
|
24
|
+
"""Detect available hardware and return a capabilities dictionary.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Dictionary containing:
|
|
28
|
+
- device: str — best available device name ("cuda", "mps", "cpu")
|
|
29
|
+
- cuda_available: bool — whether CUDA is available
|
|
30
|
+
- mps_available: bool — whether Apple MPS is available
|
|
31
|
+
- gpu_name: str | None — name of the CUDA GPU (if available)
|
|
32
|
+
- gpu_memory: int | None — total GPU memory in bytes (if available)
|
|
33
|
+
- compute_capability: tuple[int, int] | None — CUDA compute cap
|
|
34
|
+
- cuda_version: str | None — CUDA toolkit version string
|
|
35
|
+
- device_count: int — number of CUDA devices
|
|
36
|
+
"""
|
|
37
|
+
cuda_available = torch.cuda.is_available()
|
|
38
|
+
mps_available = hasattr(torch.backends, "mps") and torch.backends.mps.is_available()
|
|
39
|
+
|
|
40
|
+
gpu_name: str | None = None
|
|
41
|
+
gpu_memory: int | None = None
|
|
42
|
+
compute_capability: tuple[int, int] | None = None
|
|
43
|
+
cuda_version: str | None = None
|
|
44
|
+
device_count = 0
|
|
45
|
+
|
|
46
|
+
if cuda_available:
|
|
47
|
+
device_count = torch.cuda.device_count()
|
|
48
|
+
try:
|
|
49
|
+
gpu_name = torch.cuda.get_device_name(0)
|
|
50
|
+
except Exception:
|
|
51
|
+
gpu_name = None
|
|
52
|
+
try:
|
|
53
|
+
props = torch.cuda.get_device_properties(0)
|
|
54
|
+
gpu_memory = props.total_mem
|
|
55
|
+
compute_capability = (props.major, props.minor)
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
cuda_version = torch.version.cuda
|
|
59
|
+
|
|
60
|
+
# Determine best device string
|
|
61
|
+
if cuda_available:
|
|
62
|
+
device = "cuda"
|
|
63
|
+
elif mps_available:
|
|
64
|
+
device = "mps"
|
|
65
|
+
else:
|
|
66
|
+
device = "cpu"
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
"device": device,
|
|
70
|
+
"cuda_available": cuda_available,
|
|
71
|
+
"mps_available": mps_available,
|
|
72
|
+
"gpu_name": gpu_name,
|
|
73
|
+
"gpu_memory": gpu_memory,
|
|
74
|
+
"compute_capability": compute_capability,
|
|
75
|
+
"cuda_version": cuda_version,
|
|
76
|
+
"device_count": device_count,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def best_device() -> torch.device:
|
|
81
|
+
"""Return the best available torch.device.
|
|
82
|
+
|
|
83
|
+
Priority: CUDA > MPS > CPU.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
torch.device for the best available hardware.
|
|
87
|
+
"""
|
|
88
|
+
if torch.cuda.is_available():
|
|
89
|
+
return torch.device("cuda")
|
|
90
|
+
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
|
|
91
|
+
return torch.device("mps")
|
|
92
|
+
return torch.device("cpu")
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""PyTorch fallback implementations for all USN kernels.
|
|
2
|
+
|
|
3
|
+
Provides Level 2 (torch.compile) wrappers and registers them with
|
|
4
|
+
AccelerationManager. When torch.compile is available, these are
|
|
5
|
+
JIT-compiled for better performance. When unavailable, they delegate
|
|
6
|
+
directly to the Level 4 (eager) implementations.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
from usn.backends.acceleration import AccelerationLevel, AccelerationManager
|
|
16
|
+
from usn.backends.triton_kernels import (
|
|
17
|
+
eager_channel_mlp,
|
|
18
|
+
eager_fused_projections,
|
|
19
|
+
eager_state_core,
|
|
20
|
+
eager_temporal_gate,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
# Check if torch.compile is available
|
|
26
|
+
_HAS_COMPILE = hasattr(torch, "compile")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _try_compile(fn):
|
|
30
|
+
"""Attempt to wrap a function with torch.compile.
|
|
31
|
+
|
|
32
|
+
Uses 'reduce-overhead' mode which is optimized for repeated calls
|
|
33
|
+
with the same shapes (common in autoregressive generation and
|
|
34
|
+
fixed-length training batches).
|
|
35
|
+
|
|
36
|
+
If torch.compile is unavailable or compilation fails for any reason,
|
|
37
|
+
returns the original function unchanged — ensuring graceful fallback
|
|
38
|
+
to eager execution.
|
|
39
|
+
|
|
40
|
+
Note: torch.compile is lazy — errors may occur at first invocation
|
|
41
|
+
rather than at wrap time. The wrapper catches these runtime compilation
|
|
42
|
+
failures and falls back to the original function transparently.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
fn: The function to compile.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Compiled function if torch.compile is available and succeeds,
|
|
49
|
+
otherwise the original function unchanged.
|
|
50
|
+
"""
|
|
51
|
+
if _HAS_COMPILE:
|
|
52
|
+
try:
|
|
53
|
+
compiled = torch.compile(fn, mode="reduce-overhead")
|
|
54
|
+
except Exception:
|
|
55
|
+
logger.debug(
|
|
56
|
+
"torch.compile wrapping failed for %s, using eager",
|
|
57
|
+
getattr(fn, "__name__", str(fn)),
|
|
58
|
+
)
|
|
59
|
+
return fn
|
|
60
|
+
|
|
61
|
+
# Wrap to catch lazy compilation failures at first call
|
|
62
|
+
def _safe_compiled(*args, **kwargs):
|
|
63
|
+
try:
|
|
64
|
+
return compiled(*args, **kwargs)
|
|
65
|
+
except Exception:
|
|
66
|
+
logger.debug(
|
|
67
|
+
"torch.compile execution failed for %s, falling back to eager",
|
|
68
|
+
getattr(fn, "__name__", str(fn)),
|
|
69
|
+
)
|
|
70
|
+
# Replace ourselves with the original to avoid repeated failures
|
|
71
|
+
_safe_compiled.__wrapped_fallback__ = True
|
|
72
|
+
return fn(*args, **kwargs)
|
|
73
|
+
|
|
74
|
+
# Preserve metadata for introspection
|
|
75
|
+
_safe_compiled.__name__ = getattr(fn, "__name__", "compiled_fn")
|
|
76
|
+
_safe_compiled.__qualname__ = getattr(fn, "__qualname__", "compiled_fn")
|
|
77
|
+
_safe_compiled.__doc__ = fn.__doc__
|
|
78
|
+
_safe_compiled.__wrapped__ = fn
|
|
79
|
+
_safe_compiled.__wrapped_fallback__ = False
|
|
80
|
+
return _safe_compiled
|
|
81
|
+
|
|
82
|
+
return fn
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Compiled versions (Level 2)
|
|
86
|
+
compiled_projections = _try_compile(eager_fused_projections)
|
|
87
|
+
compiled_temporal_gate = _try_compile(eager_temporal_gate)
|
|
88
|
+
compiled_state_core = _try_compile(eager_state_core)
|
|
89
|
+
compiled_channel_mlp = _try_compile(eager_channel_mlp)
|
|
90
|
+
|
|
91
|
+
# Register Level 2 (COMPILE) kernels
|
|
92
|
+
AccelerationManager.register_kernel("projections", AccelerationLevel.COMPILE, compiled_projections)
|
|
93
|
+
AccelerationManager.register_kernel(
|
|
94
|
+
"temporal_gate", AccelerationLevel.COMPILE, compiled_temporal_gate
|
|
95
|
+
)
|
|
96
|
+
AccelerationManager.register_kernel("state_core", AccelerationLevel.COMPILE, compiled_state_core)
|
|
97
|
+
AccelerationManager.register_kernel("channel_mlp", AccelerationLevel.COMPILE, compiled_channel_mlp)
|
|
98
|
+
|
|
99
|
+
logger.debug(
|
|
100
|
+
"Registered Level 2 (COMPILE) fallback kernels (torch.compile %s)",
|
|
101
|
+
"available" if _HAS_COMPILE else "NOT available — using eager",
|
|
102
|
+
)
|