sunnynet-cx 0.3.0__py3-none-win_amd64.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 (74) hide show
  1. sunnynet_cx-0.3.0.data/purelib/sunnynet/__init__.py +29 -0
  2. sunnynet_cx-0.3.0.data/purelib/sunnynet/_native.py +277 -0
  3. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sources.sha256 +50 -0
  4. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_abi.dll +0 -0
  5. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_deltanet.dll +0 -0
  6. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_gla.dll +0 -0
  7. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_hgrn2.dll +0 -0
  8. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_kernels.dll +0 -0
  9. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_mamba2.dll +0 -0
  10. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_model.dll +0 -0
  11. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_quant.dll +0 -0
  12. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_runtime.dll +0 -0
  13. sunnynet_cx-0.3.0.data/purelib/sunnynet/_prebuilt/win-x86_64/sunnynet_rwkv7.dll +0 -0
  14. sunnynet_cx-0.3.0.data/purelib/sunnynet/baseline.py +193 -0
  15. sunnynet_cx-0.3.0.data/purelib/sunnynet/cli.py +536 -0
  16. sunnynet_cx-0.3.0.data/purelib/sunnynet/init.py +39 -0
  17. sunnynet_cx-0.3.0.data/purelib/sunnynet/integrate.py +379 -0
  18. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/__init__.py +184 -0
  19. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/core_auto.py +115 -0
  20. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/core_avx2.c +149 -0
  21. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/scan_auto.py +73 -0
  22. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/scan_avx2.c +99 -0
  23. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/scan_avx512.c +140 -0
  24. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/scan_neon.c +205 -0
  25. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/simd.py +227 -0
  26. sunnynet_cx-0.3.0.data/purelib/sunnynet/kernels/step_avx2.c +87 -0
  27. sunnynet_cx-0.3.0.data/purelib/sunnynet/layers.py +440 -0
  28. sunnynet_cx-0.3.0.data/purelib/sunnynet/linear_baselines.py +550 -0
  29. sunnynet_cx-0.3.0.data/purelib/sunnynet/model.py +175 -0
  30. sunnynet_cx-0.3.0.data/purelib/sunnynet/ops.py +126 -0
  31. sunnynet_cx-0.3.0.data/purelib/sunnynet/profiling.py +264 -0
  32. sunnynet_cx-0.3.0.data/purelib/sunnynet/release.py +226 -0
  33. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/__init__.py +236 -0
  34. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/abi.py +200 -0
  35. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/deltanet.c +442 -0
  36. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/deltanet.h +40 -0
  37. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/deltanet.py +186 -0
  38. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/deltanet_c.py +144 -0
  39. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/gguf.py +104 -0
  40. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/gla.c +422 -0
  41. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/gla.h +43 -0
  42. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/gla.py +180 -0
  43. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/gla_c.py +143 -0
  44. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/hgrn2.c +460 -0
  45. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/hgrn2.h +46 -0
  46. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/hgrn2.py +317 -0
  47. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/hgrn2_c.py +143 -0
  48. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/mamba2.c +337 -0
  49. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/mamba2.h +38 -0
  50. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/mamba2.py +197 -0
  51. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/mamba2_c.py +142 -0
  52. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/model.py +140 -0
  53. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/quant.c +173 -0
  54. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/quant.h +35 -0
  55. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/quant.py +120 -0
  56. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/recur.c +257 -0
  57. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/recur.h +64 -0
  58. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/rwkv7.c +508 -0
  59. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/rwkv7.h +33 -0
  60. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/rwkv7.py +241 -0
  61. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/rwkv7_c.py +130 -0
  62. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/sn_abi.c +136 -0
  63. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/sn_abi.h +105 -0
  64. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/sunnynet.c +579 -0
  65. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/sunnynet.h +52 -0
  66. sunnynet_cx-0.3.0.data/purelib/sunnynet/runtime/vocab.py +91 -0
  67. sunnynet_cx-0.3.0.data/purelib/sunnynet/serve.py +501 -0
  68. sunnynet_cx-0.3.0.data/purelib/sunnynet/tokenizer.py +227 -0
  69. sunnynet_cx-0.3.0.dist-info/METADATA +1045 -0
  70. sunnynet_cx-0.3.0.dist-info/RECORD +74 -0
  71. sunnynet_cx-0.3.0.dist-info/WHEEL +5 -0
  72. sunnynet_cx-0.3.0.dist-info/entry_points.txt +5 -0
  73. sunnynet_cx-0.3.0.dist-info/licenses/LICENSE +202 -0
  74. sunnynet_cx-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,29 @@
1
+ """SunnyNet: 原创 CPU 通用序列神经网络架构."""
2
+ import importlib
3
+
4
+ __version__ = "0.3.0"
5
+ __all__ = ["SunnyConfig", "SunnyNet", "CenteredScaleNorm", "logspace_scan",
6
+ "poly_sigmoid", "softwedge"]
7
+
8
+ # 延迟导入 torch: kernels/runtime 子包不依赖它, 顶层 import 会让二者无法单独导入。
9
+ _LAZY = {
10
+ "SunnyConfig": "sunnynet.model",
11
+ "SunnyNet": "sunnynet.model",
12
+ "CenteredScaleNorm": "sunnynet.ops",
13
+ "logspace_scan": "sunnynet.ops",
14
+ "poly_sigmoid": "sunnynet.ops",
15
+ "softwedge": "sunnynet.ops",
16
+ }
17
+
18
+
19
+ def __getattr__(name):
20
+ mod = _LAZY.get(name)
21
+ if mod is None:
22
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
23
+ val = getattr(importlib.import_module(mod), name)
24
+ globals()[name] = val
25
+ return val
26
+
27
+
28
+ def __dir__():
29
+ return sorted(set(__all__) | set(globals()))
@@ -0,0 +1,277 @@
1
+ """预编译共享库的定位与构建.
2
+
3
+ 包内 sunnynet/_prebuilt/<平台标签>/ 存放构建期编好的产物。运行期优先加载它,
4
+ 命中就不再要求本机有 C 编译器; 未命中或 CPU 指令集不达标时退回源码编译。
5
+
6
+ 预编译库一律以 -mavx2 -mfma -march=x86-64-v3 编出, 只对 x86-64 且支持 AVX2 的
7
+ 机器可用, 故按指令集判定而非仅按平台。
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import platform
13
+ import shutil
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ __all__ = [
18
+ "PREBUILT", "platform_tag", "cpu_flags", "isa_ok",
19
+ "locate", "build_prebuilt", "MODULES", "SOURCES", "run_cc",
20
+ ]
21
+
22
+ HERE = Path(__file__).resolve().parent
23
+ PREBUILT = HERE / "_prebuilt"
24
+
25
+ # 构建期需要产出预编译库的模块; 每个模块需有 _compile(gcc) 与 DLL 属性。
26
+ MODULES = (
27
+ "sunnynet.runtime",
28
+ "sunnynet.runtime.model",
29
+ "sunnynet.runtime.rwkv7_c",
30
+ "sunnynet.runtime.deltanet_c",
31
+ "sunnynet.runtime.mamba2_c",
32
+ "sunnynet.runtime.gla_c",
33
+ "sunnynet.runtime.hgrn2_c",
34
+ "sunnynet.runtime.quant",
35
+ "sunnynet.runtime.abi",
36
+ "sunnynet.kernels",
37
+ )
38
+
39
+ # 每个预编译库的输入源码 (相对 HERE)。随包分发, 用于判定产物是否过期。
40
+ SOURCES = {
41
+ "sunnynet.runtime": ("runtime/recur.c", "runtime/recur.h"),
42
+ "sunnynet.runtime.model": ("runtime/sunnynet.c", "runtime/sunnynet.h"),
43
+ "sunnynet.runtime.rwkv7_c": ("runtime/rwkv7.c", "runtime/rwkv7.h",
44
+ "runtime/quant.c"),
45
+ "sunnynet.runtime.deltanet_c": ("runtime/deltanet.c", "runtime/deltanet.h",
46
+ "runtime/quant.c"),
47
+ "sunnynet.runtime.mamba2_c": ("runtime/mamba2.c", "runtime/mamba2.h"),
48
+ "sunnynet.runtime.gla_c": ("runtime/gla.c", "runtime/gla.h",
49
+ "runtime/quant.c"),
50
+ "sunnynet.runtime.hgrn2_c": ("runtime/hgrn2.c", "runtime/hgrn2.h",
51
+ "runtime/quant.c"),
52
+ "sunnynet.runtime.quant": ("runtime/quant.c", "runtime/quant.h"),
53
+ "sunnynet.runtime.abi": ("runtime/sn_abi.c", "runtime/sn_abi.h",
54
+ "runtime/recur.c", "runtime/rwkv7.c",
55
+ "runtime/quant.c"),
56
+ "sunnynet.kernels": ("kernels/scan_avx2.c", "kernels/core_avx2.c",
57
+ "kernels/step_avx2.c"),
58
+ }
59
+
60
+
61
+ def _machine() -> str:
62
+ m = platform.machine().lower()
63
+ return {"amd64": "x86_64", "arm64": "aarch64"}.get(m, m)
64
+
65
+
66
+ def run_cc(cmd: list[str]):
67
+ """跑编译器。Windows 下 gcc 的诊断按系统代码页输出, 中文路径会撞非 UTF-8 字节;
68
+ 带 text=True 会抛 UnicodeDecodeError 把真实编译错误盖掉, 故收 bytes 再容错解码。
69
+ 返回 (returncode, 合并后的诊断文本)。
70
+ """
71
+ import subprocess
72
+
73
+ r = subprocess.run(cmd, capture_output=True)
74
+ msg = (r.stderr or r.stdout or b"").decode("utf-8", errors="replace")
75
+ return r.returncode, msg
76
+
77
+
78
+ def platform_tag() -> str:
79
+ """对应 _prebuilt 下的子目录名, 如 win-x86_64 / linux-x86_64。"""
80
+ if sys.platform == "win32":
81
+ return f"win-{_machine()}"
82
+ if sys.platform == "darwin":
83
+ return f"macos-{_machine()}"
84
+ return f"linux-{_machine()}"
85
+
86
+
87
+ def cpu_flags() -> set[str]:
88
+ """本机 CPU 特性名集合 (小写)。"""
89
+ if sys.platform != "win32":
90
+ try:
91
+ text = Path("/proc/cpuinfo").read_text(encoding="utf-8",
92
+ errors="replace")
93
+ except OSError:
94
+ return set()
95
+ for line in text.splitlines():
96
+ low = line.lower()
97
+ if low.startswith("flags") or low.startswith("features"):
98
+ return set(line.split(":", 1)[1].split())
99
+ return set()
100
+ try:
101
+ import ctypes
102
+ import ctypes.wintypes as wt
103
+
104
+ k = ctypes.windll.kernel32
105
+ k.IsProcessorFeaturePresent.argtypes = [wt.DWORD]
106
+ k.IsProcessorFeaturePresent.restype = wt.BOOL
107
+ # PF_AVX2_INSTRUCTIONS_AVAILABLE = 40。Windows 无独立的 FMA 查询位,
108
+ # 但 FMA3 与 AVX2 同期引入 (Haswell), 有 AVX2 即有 FMA。
109
+ return {"avx2", "fma"} if k.IsProcessorFeaturePresent(40) else set()
110
+ except Exception:
111
+ return set()
112
+
113
+
114
+ _ISA_CACHE = None
115
+
116
+
117
+ def isa_ok() -> bool:
118
+ """预编译库用 x86-64-v3 编出, 只有 AVX2+FMA 的 x86-64 机器能跑。"""
119
+ global _ISA_CACHE
120
+ if _ISA_CACHE is None:
121
+ if _machine() != "x86_64":
122
+ _ISA_CACHE = False
123
+ else:
124
+ f = cpu_flags()
125
+ _ISA_CACHE = "avx2" in f and "fma" in f
126
+ return _ISA_CACHE
127
+
128
+
129
+ def find(name: str) -> Path | None:
130
+ p = PREBUILT / platform_tag() / name
131
+ return p if p.is_file() else None
132
+
133
+
134
+ def _sha256(p: Path) -> str:
135
+ import hashlib
136
+
137
+ h = hashlib.sha256()
138
+ with open(p, "rb") as f:
139
+ for blk in iter(lambda: f.read(1 << 16), b""):
140
+ h.update(blk)
141
+ return h.hexdigest()
142
+
143
+
144
+ def _manifest_path() -> Path:
145
+ return PREBUILT / platform_tag() / "sources.sha256"
146
+
147
+
148
+ def all_sources() -> tuple[str, ...]:
149
+ return tuple(sorted({s for srcs in SOURCES.values() for s in srcs}))
150
+
151
+
152
+ def _read_manifest() -> dict:
153
+ import json
154
+
155
+ p = _manifest_path()
156
+ if not p.is_file():
157
+ return {}
158
+ try:
159
+ return json.loads(p.read_text(encoding="utf-8"))
160
+ except Exception:
161
+ return {}
162
+
163
+
164
+ _STALE_CACHE = {}
165
+
166
+
167
+ def prebuilt_stale(shlib_name: str | None = None) -> bool:
168
+ """包内产物是否与同包源码不一致。
169
+
170
+ 比对内容哈希而非 mtime: pip 解包会把源码 mtime 刷成解包时刻, 恒新于产物,
171
+ 按时间判会把装好的包一律判成过期。源码没随包分发时无法判定, 按不过期处理。
172
+
173
+ shlib_name 为 None 时检查全部库 (需 import 各模块), 否则只查清单里该库的条目
174
+ ——不 import 任何模块, 少读文件: Windows 实时防护下每次打开约 6 ms。
175
+ """
176
+ if shlib_name is None:
177
+ return any(prebuilt_stale(n) for n in _dll_names())
178
+ if shlib_name in _STALE_CACHE:
179
+ return _STALE_CACHE[shlib_name]
180
+ man = _read_manifest().get(shlib_name) or {}
181
+ stale = False
182
+ for rel, want in man.items():
183
+ p = HERE / rel
184
+ if p.is_file() and _sha256(p) != want:
185
+ stale = True
186
+ break
187
+ _STALE_CACHE[shlib_name] = stale
188
+ return stale
189
+
190
+
191
+ def _dll_names() -> tuple[str, ...]:
192
+ out = []
193
+ for name in MODULES:
194
+ d = _dll_of(name)
195
+ if d:
196
+ out.append(d)
197
+ return tuple(out)
198
+
199
+
200
+ def _dll_of(mod_name: str) -> str | None:
201
+ import importlib
202
+
203
+ try:
204
+ return importlib.import_module(mod_name).DLL.name
205
+ except Exception:
206
+ return None
207
+
208
+
209
+ def locate(shlib_name: str) -> Path | None:
210
+ """预编译库可加载则返回路径, 否则 None (调用方退回源码编译)。
211
+
212
+ 设 SUNNY_NO_PREBUILT=1 可强制走源码路径, 用于对照验证。
213
+ 开发态下源码改过则判过期, 避免静默加载旧库 (见 prebuilt_stale)。
214
+ """
215
+ if os.environ.get("SUNNY_NO_PREBUILT") == "1" or not isa_ok():
216
+ return None
217
+ if prebuilt_stale(shlib_name):
218
+ return None
219
+ return find(shlib_name)
220
+
221
+
222
+ def build_prebuilt(out_dir: str | Path | None = None):
223
+ """编译全部原生库并复制到 _prebuilt/<平台标签>/。
224
+
225
+ 同时写出 sources.sha256 清单, 供运行期判定产物是否与源码一致。
226
+ 返回 (产物列表, 错误说明)。当前 C 运行时用 -mavx2 编出, 只有 x86-64 能编;
227
+ 其他架构返回空列表并说明, 由调用方决定是否视为失败。
228
+
229
+ 编译落在临时目录: 各模块的 _compile 写的是共享的 <pkg>/_build/<dll>, 该文件
230
+ 一旦被本进程 (或别的测试) 加载过, Windows 不允许覆盖写, 编译必失败。
231
+ """
232
+ import importlib
233
+ import json
234
+ import tempfile
235
+
236
+ if _machine() != "x86_64":
237
+ return [], f"{_machine()} 不是 x86-64, 该架构无预编译产物"
238
+ if not isa_ok():
239
+ return [], "构建机不支持 AVX2+FMA, 跳过预编译"
240
+
241
+ out = Path(out_dir) if out_dir else PREBUILT / platform_tag()
242
+ made: list[Path] = []
243
+ skipped: list[str] = []
244
+ with tempfile.TemporaryDirectory(prefix="sunnynet-build-") as td:
245
+ tmp = Path(td)
246
+ for name in MODULES:
247
+ mod = importlib.import_module(name)
248
+ gcc = mod._find_gcc()
249
+ if gcc is None:
250
+ return made, f"未找到 C 编译器, 无法生成预编译库 ({name})"
251
+ dst = tmp / mod.DLL.name
252
+ cmd = mod._link_cmd(gcc, dst)
253
+ rc, msg = run_cc(cmd)
254
+ if rc != 0:
255
+ return made, f"{name} 编译失败: {msg[-500:]}"
256
+ out.mkdir(parents=True, exist_ok=True)
257
+ try:
258
+ shutil.copy2(dst, out / dst.name)
259
+ except PermissionError:
260
+ # 目标 dll 已被本进程加载, Windows 不允许覆盖。编译产物仍是新的,
261
+ # 单独说明而不整体失败 —— 其余库照常写出。
262
+ skipped.append(f"{dst.name} (已加载, 未覆盖)")
263
+ continue
264
+ made.append(out / dst.name)
265
+
266
+ man = {}
267
+ for name in MODULES:
268
+ dll = importlib.import_module(name).DLL.name
269
+ man[dll] = {rel: _sha256(HERE / rel) for rel in SOURCES[name]
270
+ if (HERE / rel).is_file()}
271
+ man_p = out / "sources.sha256"
272
+ man_p.write_text(json.dumps(man, indent=1, sort_keys=True), encoding="utf-8")
273
+ made.append(man_p)
274
+ _STALE_CACHE.clear() # 产物已刷新, 旧的过期判定失效
275
+ if skipped:
276
+ return made, "部分库被占用未覆盖: " + ", ".join(skipped)
277
+ return made, None
@@ -0,0 +1,50 @@
1
+ {
2
+ "sunnynet_abi.dll": {
3
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd",
4
+ "runtime/recur.c": "f9d528a311513322f083e5ae6dbdc60d160fcf6f529360535cb68127e6869529",
5
+ "runtime/rwkv7.c": "12cf26f74bf6399b6698695647da4ac7da794584827c13646568e8a4bea30bf0",
6
+ "runtime/sn_abi.c": "761c48ef2649f3d10e470cbf406df019b3512dccc8e7d4fff2c1d1501eaa2d14",
7
+ "runtime/sn_abi.h": "0a737b7b73c88150246a86d970169bac45300068e735883e4261317dcaa33fba"
8
+ },
9
+ "sunnynet_deltanet.dll": {
10
+ "runtime/deltanet.c": "835fb36b332d35815ec88e95e1a4988fc0d2089ac1f7bac7e2f4c593a1303ed4",
11
+ "runtime/deltanet.h": "672a3a4606e38ddb1c6c084b0a250d90d33616f78bef9d445ea5a111aee6afde",
12
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd"
13
+ },
14
+ "sunnynet_gla.dll": {
15
+ "runtime/gla.c": "bb823bd1d9e7dafefee791359efbcc4be4d77a3bd2a1442733a6bd7e1827f4aa",
16
+ "runtime/gla.h": "2889fa80d22d14b3ee82304d3a5edc0bc2fc681afa3112585e2bc817a9abaaab",
17
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd"
18
+ },
19
+ "sunnynet_hgrn2.dll": {
20
+ "runtime/hgrn2.c": "05b6b95e70d347fa2b323e3d725a3d0a0f863cf1edb37387e44a1a38dd712f32",
21
+ "runtime/hgrn2.h": "cfeddcedd70cc4dca5a948a8cd8dcf0010b59ae5e70114ad80d3bf3f6170bc8c",
22
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd"
23
+ },
24
+ "sunnynet_kernels.dll": {
25
+ "kernels/core_avx2.c": "bd65d3ff52820af3e59436759bb265d06a509ad1d803651227060cc0e471c02b",
26
+ "kernels/scan_avx2.c": "d994843e167c8cb934be1e42e752ff4a76386508098e35525cd284bc277554d9",
27
+ "kernels/step_avx2.c": "3bb28f7bd393e2eebc5444cdce2e29544fedd1a21b91cbeff3ee9fb3c3bfc426"
28
+ },
29
+ "sunnynet_mamba2.dll": {
30
+ "runtime/mamba2.c": "6f27ea4f7efafa30fb89f27fa4b2c5c6121b806eeeb5fc885b671c5c46507bbd",
31
+ "runtime/mamba2.h": "5cbd41e1d78d3390c498f619ab8310c66e9009f317b3b05bbb77541a115dfe0b"
32
+ },
33
+ "sunnynet_model.dll": {
34
+ "runtime/sunnynet.c": "705cbc064f35432f05738c8654fe6a9ddf0df6330c8e5e97e3b209dc6660d64a",
35
+ "runtime/sunnynet.h": "e391bfc7651bdf650be0886c168a7c357452976a1d74b8b1ffc047e7b1a48fda"
36
+ },
37
+ "sunnynet_quant.dll": {
38
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd",
39
+ "runtime/quant.h": "5cfd9571036c33064d692fc08043addd47b4498f66af46c113c258168e474983"
40
+ },
41
+ "sunnynet_runtime.dll": {
42
+ "runtime/recur.c": "f9d528a311513322f083e5ae6dbdc60d160fcf6f529360535cb68127e6869529",
43
+ "runtime/recur.h": "10c8d059f5296ecf7a81b816c37f6e2cf4b29e0e3eccddfac61a33c5dbcd51a9"
44
+ },
45
+ "sunnynet_rwkv7.dll": {
46
+ "runtime/quant.c": "024f36c5ee82fa97e330e982048422a0fc13a82d3539a957e64299ab82b557dd",
47
+ "runtime/rwkv7.c": "12cf26f74bf6399b6698695647da4ac7da794584827c13646568e8a4bea30bf0",
48
+ "runtime/rwkv7.h": "7a8ca76b7c2f4cc9d6fb927a8072818090042568af26af5a343bdb3af91b9c3f"
49
+ }
50
+ }
@@ -0,0 +1,193 @@
1
+ """对照基线: 标准 Transformer 语言模型.
2
+
3
+ 仅用于公平对照 (同参数量 / 同数据 / 同硬件 / 同 token 预算), 不属于 SunnyNet 架构本体.
4
+ 手写实现以便精确控制参数量与结构, 不调用 nn.TransformerEncoder.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import math
9
+ from dataclasses import dataclass
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+ import torch.nn.functional as F
14
+
15
+ __all__ = ["BaselineConfig", "TransformerLM", "TransformerState", "solve_baseline_for_params"]
16
+
17
+
18
+ @dataclass
19
+ class BaselineConfig:
20
+ vocab_size: int = 12000
21
+ d_model: int = 320
22
+ n_heads: int = 8
23
+ n_layers: int = 6
24
+ d_ff: int = 640
25
+ max_len: int = 2048
26
+ tie_embeddings: bool = True
27
+
28
+
29
+ @dataclass
30
+ class TransformerState:
31
+ """KV cache: 逐层 (k, v) 均为 (B,H,T_past,d_head), 首步前为 None."""
32
+
33
+ kv: list
34
+ pos: int = 0
35
+
36
+
37
+ class CausalSelfAttention(nn.Module):
38
+ def __init__(self, cfg: BaselineConfig):
39
+ super().__init__()
40
+ self.n_heads = cfg.n_heads
41
+ self.d_head = cfg.d_model // cfg.n_heads
42
+ self.qkv = nn.Linear(cfg.d_model, 3 * cfg.d_model, bias=True)
43
+ self.proj = nn.Linear(cfg.d_model, cfg.d_model, bias=True)
44
+
45
+ def forward(self, x, mask):
46
+ B, T, C = x.shape
47
+ q, k, v = self.qkv(x).split(C, dim=-1)
48
+ q = q.view(B, T, self.n_heads, self.d_head).transpose(1, 2)
49
+ k = k.view(B, T, self.n_heads, self.d_head).transpose(1, 2)
50
+ v = v.view(B, T, self.n_heads, self.d_head).transpose(1, 2)
51
+ att = (q @ k.transpose(-2, -1)) / math.sqrt(self.d_head)
52
+ att = att.masked_fill(mask[:, :, :T, :T] == 0, float("-inf"))
53
+ att = F.softmax(att, dim=-1)
54
+ y = (att @ v).transpose(1, 2).contiguous().view(B, T, C)
55
+ return self.proj(y)
56
+
57
+ def step(self, x_t, cache):
58
+ """单步: x_t (B,C), cache=(k,v) 形如 (B,H,T_past,d_head); 返回 (y, cache_new)."""
59
+ B, C = x_t.shape
60
+ q, k, v = self.qkv(x_t).split(C, dim=-1)
61
+ q = q.view(B, 1, self.n_heads, self.d_head).transpose(1, 2)
62
+ k = k.view(B, 1, self.n_heads, self.d_head).transpose(1, 2)
63
+ v = v.view(B, 1, self.n_heads, self.d_head).transpose(1, 2)
64
+ if cache is not None:
65
+ k = torch.cat([cache[0], k], dim=2)
66
+ v = torch.cat([cache[1], v], dim=2)
67
+ att = (q @ k.transpose(-2, -1)) / math.sqrt(self.d_head)
68
+ att = F.softmax(att, dim=-1)
69
+ y = (att @ v).transpose(1, 2).contiguous().view(B, 1, C)
70
+ return self.proj(y)[:, 0], (k, v)
71
+
72
+
73
+ class Block(nn.Module):
74
+ def __init__(self, cfg: BaselineConfig):
75
+ super().__init__()
76
+ self.ln1 = nn.LayerNorm(cfg.d_model)
77
+ self.attn = CausalSelfAttention(cfg)
78
+ self.ln2 = nn.LayerNorm(cfg.d_model)
79
+ self.fc1 = nn.Linear(cfg.d_model, cfg.d_ff, bias=True)
80
+ self.fc2 = nn.Linear(cfg.d_ff, cfg.d_model, bias=True)
81
+
82
+ def forward(self, x, mask):
83
+ x = x + self.attn(self.ln1(x), mask)
84
+ h = F.gelu(self.fc1(self.ln2(x)))
85
+ return x + self.fc2(h)
86
+
87
+ def step(self, x_t, cache):
88
+ y, c = self.attn.step(self.ln1(x_t), cache)
89
+ x_t = x_t + y
90
+ return x_t + self.fc2(F.gelu(self.fc1(self.ln2(x_t)))), c
91
+
92
+
93
+ class TransformerLM(nn.Module):
94
+ def __init__(self, cfg: BaselineConfig):
95
+ super().__init__()
96
+ self.cfg = cfg
97
+ self.embed = nn.Embedding(cfg.vocab_size, cfg.d_model)
98
+ self.pos = nn.Embedding(cfg.max_len, cfg.d_model)
99
+ self.blocks = nn.ModuleList([Block(cfg) for _ in range(cfg.n_layers)])
100
+ self.ln_f = nn.LayerNorm(cfg.d_model)
101
+ self.head = nn.Linear(cfg.d_model, cfg.vocab_size, bias=False)
102
+ if cfg.tie_embeddings:
103
+ self.head.weight = self.embed.weight
104
+ mask = torch.tril(torch.ones(cfg.max_len, cfg.max_len)).view(1, 1, cfg.max_len, cfg.max_len)
105
+ self.register_buffer("mask", mask, persistent=False)
106
+ self.apply(self._init)
107
+ for n, p in self.named_parameters():
108
+ if n.endswith("proj.weight") or n.endswith("fc2.weight"):
109
+ nn.init.normal_(p, mean=0.0, std=0.02 / math.sqrt(2 * cfg.n_layers))
110
+
111
+ def _init(self, m):
112
+ if isinstance(m, nn.Linear):
113
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
114
+ if m.bias is not None:
115
+ nn.init.zeros_(m.bias)
116
+ elif isinstance(m, nn.Embedding):
117
+ nn.init.normal_(m.weight, mean=0.0, std=0.02)
118
+
119
+ def num_params(self, non_embedding: bool = False) -> int:
120
+ n = sum(p.numel() for p in self.parameters())
121
+ if non_embedding:
122
+ n -= self.embed.weight.numel()
123
+ n -= self.pos.weight.numel()
124
+ if not self.cfg.tie_embeddings:
125
+ n -= self.head.weight.numel()
126
+ return n
127
+
128
+ def forward(self, idx, state=None, return_state=False):
129
+ B, T = idx.shape
130
+ pos = torch.arange(T, device=idx.device)
131
+ x = self.embed(idx) + self.pos(pos)[None, :, :]
132
+ for blk in self.blocks:
133
+ x = blk(x, self.mask)
134
+ logits = self.head(self.ln_f(x))
135
+ if return_state:
136
+ return logits, None
137
+ return logits
138
+
139
+ @torch.no_grad()
140
+ def forward_step(self, idx_t, state):
141
+ """单步自回归 (KV cache): idx_t (B,) -> (logits (B,V), state)."""
142
+ if state is None:
143
+ state = TransformerState(kv=[None] * len(self.blocks), pos=0)
144
+ x = self.embed(idx_t) + self.pos(torch.tensor(state.pos, device=idx_t.device))
145
+ new_kv = []
146
+ for blk, c in zip(self.blocks, state.kv):
147
+ x, cn = blk.step(x, c)
148
+ new_kv.append(cn)
149
+ logits = self.head(self.ln_f(x))
150
+ return logits, TransformerState(kv=new_kv, pos=state.pos + 1)
151
+
152
+
153
+ def stack_param_count(d_model: int, d_ff: int, n_layers: int) -> int:
154
+ """Transformer 计算栈参数量, 不含 token/位置嵌入与 LM head.
155
+
156
+ SunnyNet 无位置嵌入, 只有排除位置嵌入两边口径才一致.
157
+ """
158
+ per_layer = (d_model * 3 * d_model + 3 * d_model
159
+ + d_model * d_model + d_model
160
+ + 2 * d_model
161
+ + d_model * d_ff + d_ff
162
+ + d_ff * d_model + d_model
163
+ + 2 * d_model)
164
+ return n_layers * per_layer + 2 * d_model
165
+
166
+
167
+ def solve_baseline_for_params(target_non_embed: int, vocab_size: int,
168
+ n_layers: int = 6, n_heads: int = 8,
169
+ max_len: int = 2048):
170
+ """枚举 (d_model, d_ff) 使计算栈参数量最接近目标. 用解析公式, 不实例化模型.
171
+
172
+ 返回 (cfg, 实际栈参数量, 相对偏差).
173
+ """
174
+ best = None
175
+ for d_model in range(64, 3072, 8):
176
+ if d_model % n_heads:
177
+ continue
178
+ base = (n_layers * (d_model * 3 * d_model + 3 * d_model
179
+ + d_model * d_model + d_model + 2 * d_model
180
+ + d_model * d_model + d_model + 2 * d_model)
181
+ + 2 * d_model)
182
+ if base > target_non_embed * 1.05:
183
+ break
184
+ for d_ff in range(d_model, 6 * d_model + 1, 16):
185
+ n = stack_param_count(d_model, d_ff, n_layers)
186
+ rel = abs(n - target_non_embed) / max(1, target_non_embed)
187
+ key = (rel, -min(n, target_non_embed))
188
+ if best is None or key < best[0]:
189
+ best = (key, n, rel, BaselineConfig(
190
+ vocab_size=vocab_size, d_model=d_model, n_heads=n_heads,
191
+ n_layers=n_layers, d_ff=d_ff, max_len=max_len))
192
+ _, n, rel, cfg = best
193
+ return cfg, n, rel