Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / distributed / expert_parallel / recipes.py: 79%

67 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-25 05:45 +0800

1# Copyright 2025-2026 Huawei Technologies Co., Ltd 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================ 

15"""expert_parallel.recipes: built-in EP compute factories, organized as EXPLICIT archetypes 

16(accuracy_fix_plan.md §3 E2). 

17 

18Since the explicit-injection rework the planner only SHARDS the expert 

19parameters (``{EP: Shard(0)}`` + stacking metadata); the EP compute is never 

20injected automatically — a factory must be referenced explicitly from YAML: 

21 

22.. code-block:: yaml 

23 

24 plan_overrides: 

25 - match: "*.mlp" 

26 when: ep 

27 local_compute_fn: 

28 _target_: hyper_parallel.distributed.expert_parallel.recipes.qwen2moe_ep_compute_fn 

29 

30Archetype table (each factory is a COMPLETE semantic implementation — 

31router / dispatch / experts / combine / shared expert / gate / merge are 

32all inside; the framework never fills in any branch for you): 

33 

34=============================== ====================== =========================== ========================= 

35archetype key router shared expert representative models 

36=============================== ====================== =========================== ========================= 

37routed_only_softmax_topk softmax top-k none legacy linear routers 

38mixtral_topk_router TopKRouter tuple none Mixtral 

39qwen2moe_shared_expert_gate TopKRouter tuple shared_expert + sigmoid gate Qwen2-MoE 

40deepseekv3_sigmoid_group_shared sigmoid group routing shared_experts DeepSeek-V3 / GLM-4-MoE 

41=============================== ====================== =========================== ========================= 

42 

43Model-family archetypes live at their model-adapter homes (adjust doc §5.1): 

44the Qwen3-MoE ``qwen3moe_topk_router`` archetype is 

45``models/qwen3_moe/adapter/distributed/expert_parallel.qwen3moe_ep_compute_fn``. 

46 

47Three hard constraints on every archetype (accuracy_fix_plan.md §3.2): 

48 

491. **Explicitly keyed, zero runtime probing** — the user names the factory 

50 in YAML; the implementation accesses ``module.experts`` / 

51 ``module.shared_expert`` etc. directly. There are no getattr fallback 

52 chains anywhere (a half-found branch is a SILENT numeric error — see 

53 accuracy_problem.md 10.2). 

542. **Apply-time interface assertion** — the factory runs ONCE at apply time 

55 with the real module; it first asserts every attribute it will call and 

56 fails the build with a teaching error (listing the module's actual 

57 children) on mismatch. The assertion proves structural match, not 

58 semantic correctness (that is vouched by numeric verification). 

593. **Complete semantics** — nothing is left for the framework to guess. 

60 

61Not sure which archetype fits? ``describe_moe_module(module)`` (expert_parallel.experts) 

62prints the module's children/param shapes; ``EP_ARCHETYPE_SUGGESTIONS`` 

63maps the planner's canonical arch name to a likely archetype (a SUGGESTION 

64shown in error messages — selection stays explicit; a wrong suggestion is 

65caught by constraint 2). Non-typical MoE: copy 

66``examples/distributed/ep_factories.py`` and write your own factory with 

67the same contract. 

68 

69Nested-boundary call contract (inside every compute_fn): calling a 

70planned nested-boundary submodule (e.g. ``module.shared_expert(...)``) 

71returns its out_dst layout with TP communication already sealed inside — 

72the caller MUST NOT apply compensating collectives to the return value: 

73 

74.. code-block:: python 

75 

76 dist.all_reduce(shared, group=tp_group) # FORBIDDEN — the nested 

77 # boundary already reduced 

78 

79Factory contract: decorated ``@local_compute`` (injection discipline — 

80undecorated factories fail fast in the resolution chain). The mesh family 

81``mesh`` / ``tp_mesh`` / ``cp_mesh`` / ``ep_mesh`` is MANDATORY context, 

82ALL filled by the framework at apply time (None for inactive axes; 

83``ep_mesh`` is the same object the expert parameters were sharded on). 

84The factory must RETURN the compute fn 

85``fn(module, *local_args, **local_kwargs) -> Tensor`` executed on local 

86tensors inside the local-region skeleton. 

87""" 

88 

89from typing import Any, Callable, Optional 

90 

91import torch 

92 

93from hyper_parallel.distributed.expert_parallel.routing import ( 

94 resolve_moe_router, 

95) 

96from hyper_parallel.distributed.expert_parallel.experts import ( 

97 bind_local_expert_forward, 

98 ep_routed_dispatch, 

99 ep_routed_experts_and_combine, 

100 ep_routed_forward, 

101 require_attrs, 

102) 

103from hyper_parallel.distributed.recipe_spec import ( 

104 local_compute, 

105) 

106 

107 

108# {canonical arch name (ShardingPlanner._get_architecture): archetype key} — 

109# a SUGGESTION shown in preflight error messages and describe_moe_module 

110# output, never an automatic selection (accuracy_fix_plan.md §3 E2). 

111EP_ARCHETYPE_SUGGESTIONS = { 

112 "qwen2moe": "qwen2moe_shared_expert_gate", 

113 "qwen2_moe": "qwen2moe_shared_expert_gate", 

114 "mixtral": "mixtral_topk_router", 

115 "deepseekv3": "deepseekv3_sigmoid_group_shared", 

116 "deepseek_v3": "deepseekv3_sigmoid_group_shared", 

117 "glm4moe": "deepseekv3_sigmoid_group_shared", 

118 "glm4_moe": "deepseekv3_sigmoid_group_shared", 

119} 

120 

121 

122def _require_ep_group(ep_mesh: Any, factory_name: str): 

123 """Validate the expert mesh context and return the EP process group.""" 

124 if ep_mesh is None: 

125 raise ValueError( 

126 f"{factory_name} was built without an ep_mesh — this factory must " 

127 "be injected on an EP-sharded MoE boundary (ep_size > 1 so the " 

128 "framework-derived expert mesh exists). If you meant TP-only " 

129 "expert sharding, do not use an EP compute factory." 

130 ) 

131 return ep_mesh.get_group("ep") 

132 

133 

134def _require_moe_interface(module: Any, expected_attrs, archetype_key: str) -> None: 

135 """Apply-time structural assertion for an archetype (constraint 2).""" 

136 require_attrs(module, *expected_attrs, owner=f"archetype '{archetype_key}'") 

137 

138 

139def build_ep_compute( 

140 module: Any, 

141 ep_mesh: Any, 

142 *, 

143 router_fn: Callable, 

144 archetype_key: str, 

145 expected_attrs, 

146 combine: Callable, 

147 use_grouped_gemm: bool = False, 

148 fix_router: bool = False, 

149 capacity_factor: Optional[float] = None, 

150 overlap_fn: Optional[Callable] = None, 

151) -> Callable: 

152 """Shared skeleton for archetype factories: validate context, assert the 

153 interface, bind the local expert entry point, and close over the 

154 archetype-specific ``combine`` (the branch composition — the ONLY part 

155 that differs between archetypes). 

156 

157 ``combine(module, hidden_states, routed) -> Tensor`` receives the routed 

158 branch output and returns the MoE block output. 

159 

160 When ``overlap_fn`` is given the routed branch is split into dispatch and 

161 experts+combine, and ``overlap_fn(module, hidden_states)`` runs in between: 

162 the token exchange is issued asynchronously there, so the overlapped work 

163 (for DeepSeek-V3 style blocks the shared-expert MLP, which depends only on 

164 ``hidden_states``) proceeds while it is in flight. ``combine`` then 

165 receives that value as a fourth argument. 

166 

167 Public since M3 (adjust doc §5.4): model adapters (e.g. 

168 ``models/qwen3_moe/adapter/distributed/expert_parallel.py``) compose 

169 their EP archetype on top of this skeleton while the router adapters and 

170 dispatch primitives stay in this generic layer. 

171 ``fix_router`` is the archetype-independent benchmark switch: it swaps the chosen 

172 router for its balanced variant -- the ``<name>_fixed`` registry entry when 

173 ``router_fn`` names one, else 

174 :func:`~hyper_parallel.distributed.expert_parallel.routing.make_balanced_router` 

175 around the given callable -- so a family gets the uniform-load comparison mode 

176 without carrying its own copy of the router. See that factory for what it does and 

177 why its numbers must not be reported as an efficiency result. 

178 """ 

179 router_fn = resolve_moe_router(router_fn, fix_router) 

180 ep_group = _require_ep_group(ep_mesh, f"archetype '{archetype_key}'") 

181 _require_moe_interface(module, expected_attrs, archetype_key) 

182 if use_grouped_gemm: 

183 bind_local_expert_forward( 

184 module, 

185 ep_mesh["ep"].size(), 

186 use_grouped_gemm=True, 

187 ) 

188 else: 

189 # Keep the established call contract unchanged for every reference 

190 # archetype and for Qwen3 when grouped GEMM is disabled. 

191 bind_local_expert_forward(module, ep_mesh["ep"].size()) 

192 

193 if capacity_factor is not None: 

194 # Read back by ``experts._resolve_capacity_factor``; bounds each expert's routed slots 

195 # so the busiest rank's buffers cannot blow up under a skewed real router. 

196 module.experts.capacity_factor = float(capacity_factor) 

197 

198 if overlap_fn is None: 

199 def compute_fn(module: Any, hidden_states: torch.Tensor) -> torch.Tensor: 

200 """Run the routed branch and compose the MoE block output.""" 

201 routed = ep_routed_forward( 

202 module, hidden_states, router_fn=router_fn, ep_group=ep_group) 

203 return combine(module, hidden_states, routed) 

204 else: 

205 def compute_fn(module: Any, hidden_states: torch.Tensor) -> torch.Tensor: 

206 """Dispatch, run ``overlap_fn`` against the in-flight exchange, then finish.""" 

207 state = ep_routed_dispatch( 

208 module, hidden_states, router_fn=router_fn, ep_group=ep_group) 

209 overlapped = overlap_fn(module, hidden_states) 

210 routed = ep_routed_experts_and_combine(module, state, ep_group) 

211 return combine(module, hidden_states, routed, overlapped) 

212 

213 return compute_fn 

214 

215 

216@local_compute 

217def routed_only_ep_compute_fn( 

218 *, 

219 module: Any, 

220 mesh: Any, 

221 tp_mesh: Any, 

222 cp_mesh: Any, 

223 ep_mesh: Any, 

224 fix_router: bool = False, 

225) -> Callable: 

226 """Archetype ``routed_only_softmax_topk``: softmax top-k router, no 

227 shared expert (Mixtral-style). Output = routed branch only. 

228 

229 Expected module interface: ``gate`` (router Linear), ``experts``. 

230 

231 ``fix_router`` selects the benchmark-only balanced variant of the router 

232 (``routing.make_balanced_router``); correctness runs keep it ``False``. 

233 """ 

234 del mesh, tp_mesh, cp_mesh 

235 return build_ep_compute( 

236 module, 

237 ep_mesh, 

238 router_fn=resolve_moe_router("default", fix_router), 

239 archetype_key="routed_only_softmax_topk", 

240 expected_attrs=["gate", "experts"], 

241 combine=lambda module, hidden_states, routed: routed, 

242 ) 

243 

244 

245@local_compute 

246def mixtral_ep_compute_fn( 

247 *, 

248 module: Any, 

249 mesh: Any, 

250 tp_mesh: Any, 

251 cp_mesh: Any, 

252 ep_mesh: Any, 

253 fix_router: bool = False, 

254) -> Callable: 

255 """Archetype ``mixtral_topk_router`` with training-time router jitter. 

256 

257 Transformers 5.12 Mixtral uses a TopKRouter returning 

258 ``(logits, scores, indices)``. When configured, the original block also 

259 perturbs hidden states before both routing and expert computation. 

260 

261 ``fix_router`` selects the benchmark-only balanced variant of the router 

262 (``routing.make_balanced_router``); correctness runs keep it ``False``. 

263 """ 

264 del mesh, tp_mesh, cp_mesh 

265 router_fn = resolve_moe_router("mixtral", fix_router) 

266 ep_group = _require_ep_group(ep_mesh, "archetype 'mixtral_topk_router'") 

267 _require_moe_interface(module, ["gate", "experts"], "mixtral_topk_router") 

268 bind_local_expert_forward(module, ep_mesh["ep"].size()) 

269 

270 def compute_fn(module: Any, hidden_states: torch.Tensor) -> torch.Tensor: 

271 """Apply router jitter when training, then run the routed branch.""" 

272 jitter_noise = float(getattr(module, "jitter_noise", 0.0)) 

273 if module.training and jitter_noise > 0.0: 

274 noise = torch.empty_like(hidden_states).uniform_( 

275 1.0 - jitter_noise, 

276 1.0 + jitter_noise, 

277 ) 

278 hidden_states = hidden_states * noise 

279 return ep_routed_forward( 

280 module, 

281 hidden_states, 

282 router_fn=router_fn, 

283 ep_group=ep_group, 

284 ) 

285 

286 return compute_fn 

287 

288 

289@local_compute 

290def qwen2moe_ep_compute_fn( 

291 *, 

292 module: Any, 

293 mesh: Any, 

294 tp_mesh: Any, 

295 cp_mesh: Any, 

296 ep_mesh: Any, 

297 fix_router: bool = False, 

298) -> Callable: 

299 """Archetype ``qwen2moe_shared_expert_gate``: TopKRouter tuple + 

300 shared_expert + sigmoid scalar gate (Qwen2-MoE): 

301 ``routed + sigmoid(shared_expert_gate(x)) * shared_expert(x)``. 

302 

303 Expected module interface: ``gate``, ``experts``, ``shared_expert``, 

304 ``shared_expert_gate``. 

305 

306 ``module.shared_expert(...)`` is a planned nested TP boundary: its exit 

307 already performs the RowWise Partial TP reduction (nested-boundary call 

308 contract) — do NOT all-reduce its return value. 

309 """ 

310 del mesh, tp_mesh, cp_mesh 

311 

312 def combine( 

313 module: Any, 

314 hidden_states: torch.Tensor, 

315 routed: torch.Tensor, 

316 ) -> torch.Tensor: 

317 """Merge the routed branch with the gated shared-expert branch.""" 

318 shared = module.shared_expert(hidden_states) # nested boundary 

319 gate = torch.sigmoid(module.shared_expert_gate(hidden_states)) 

320 return routed + gate * shared 

321 

322 return build_ep_compute( 

323 module, 

324 ep_mesh, 

325 router_fn="qwen2moe", 

326 fix_router=fix_router, 

327 archetype_key="qwen2moe_shared_expert_gate", 

328 expected_attrs=["gate", "experts", "shared_expert", "shared_expert_gate"], 

329 combine=combine, 

330 ) 

331 

332 

333@local_compute 

334def deepseekv3_ep_compute_fn( 

335 *, 

336 module: Any, 

337 mesh: Any, 

338 tp_mesh: Any, 

339 cp_mesh: Any, 

340 ep_mesh: Any, 

341 use_grouped_gemm: bool = False, 

342 overlap_shared_expert: bool = False, 

343 fix_router: bool = False, 

344 capacity_factor: Optional[float] = None, 

345) -> Callable: 

346 """Archetype ``deepseekv3_sigmoid_group_shared``: sigmoid group-limited 

347 routing (with e_score_correction_bias / routed_scaling_factor, already 

348 folded into the router adapter's topk weights) + shared_experts 

349 (DeepSeek-V3 / GLM-4-MoE): ``routed + shared_experts(x)``. 

350 

351 Expected module interface: ``gate``, ``experts``, ``shared_experts``. 

352 

353 ``module.shared_experts(...)`` is a planned nested TP boundary — its 

354 exit already performs the TP reduction (nested-boundary call contract). 

355 

356 ``use_grouped_gemm=True`` runs the local experts through the packed 

357 ``gate_up_proj`` grouped GEMM path (``npu_grouped_swiglu``) instead of 

358 the eager per-expert loop; the default keeps the eager path. 

359 

360 ``overlap_shared_expert=True`` issues the token exchange asynchronously 

361 and evaluates ``shared_experts`` while it is in flight — the shared branch 

362 reads only ``hidden_states``, so it hides communication instead of 

363 serializing behind the routed branch. The math is unchanged either way. 

364 

365 ``fix_router=True`` swaps the adapter for ``deepseekv3_fixed``, a 

366 BENCHMARK-ONLY variant that hands every rank the same token count 

367 (round-robin over the K slots by destination rank instead of the gate's 

368 top-k) so that per-rank step time stops depending on how the data 

369 happened to route. Loss / grad_norm / expert-load metrics are meaningless 

370 in that mode and it must not be used to report an efficiency number — 

371 see ``routing.make_balanced_router``. Correctness runs keep it off. 

372 """ 

373 del mesh, tp_mesh, cp_mesh 

374 

375 def combine( 

376 module: Any, 

377 hidden_states: torch.Tensor, 

378 routed: torch.Tensor, 

379 overlapped: Optional[torch.Tensor] = None, 

380 ) -> torch.Tensor: 

381 """Merge the routed branch with the shared-experts branch.""" 

382 shared = module.shared_experts(hidden_states) if overlapped is None else overlapped 

383 return routed + shared # nested boundary 

384 

385 def overlap_shared(module: Any, hidden_states: torch.Tensor) -> torch.Tensor: 

386 """Shared-experts branch, evaluated while the dispatch exchange is in flight.""" 

387 return module.shared_experts(hidden_states) # nested boundary 

388 

389 return build_ep_compute( 

390 module, 

391 ep_mesh, 

392 router_fn="deepseekv3", 

393 fix_router=fix_router, 

394 archetype_key="deepseekv3_sigmoid_group_shared", 

395 expected_attrs=["gate", "experts", "shared_experts"], 

396 combine=combine, 

397 use_grouped_gemm=use_grouped_gemm, 

398 capacity_factor=capacity_factor, 

399 overlap_fn=overlap_shared if overlap_shared_expert else None, 

400 ) 

401 

402 

403# {archetype key: factory} — introspection registry (the YAML _target_ 

404# points at a factory directly; this registry exists for 

405# describe/preflight messaging). 

406EP_ARCHETYPES = { 

407 "routed_only_softmax_topk": routed_only_ep_compute_fn, 

408 "mixtral_topk_router": mixtral_ep_compute_fn, 

409 "qwen2moe_shared_expert_gate": qwen2moe_ep_compute_fn, 

410 "deepseekv3_sigmoid_group_shared": deepseekv3_ep_compute_fn, 

411}