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

67 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-11-15 07:18 +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.forward" 

26 when: ep 

27 replace: 

28 kind: method 

29 implementation: hyper_parallel.distributed.expert_parallel.recipes.qwen2moe_ep_compute_fn 

30 

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

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

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

34 

35=============================== ====================== =========================== ========================= 

36archetype key router shared expert representative models 

37=============================== ====================== =========================== ========================= 

38routed_only_softmax_topk softmax top-k none legacy linear routers 

39mixtral_topk_router TopKRouter tuple none Mixtral 

40qwen2moe_shared_expert_gate TopKRouter tuple shared_expert + sigmoid gate Qwen2-MoE 

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

42=============================== ====================== =========================== ========================= 

43 

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

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

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

47 

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

49 

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

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

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

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

54 accuracy_problem.md 10.2). 

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

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

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

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

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

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

61 

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

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

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

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

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

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

68the same contract. 

69 

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

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

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

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

74 

75.. code-block:: python 

76 

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

78 # boundary already reduced 

79 

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

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

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

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

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

85The factory must RETURN the compute fn 

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

87tensors inside the local-region skeleton. 

88""" 

89 

90from typing import Any, Callable, Optional 

91 

92import torch 

93 

94from hyper_parallel.distributed.expert_parallel.routing import ( 

95 resolve_moe_router, 

96) 

97from hyper_parallel.distributed.expert_parallel.experts import ( 

98 bind_local_expert_forward, 

99 ep_routed_dispatch, 

100 ep_routed_experts_and_combine, 

101 ep_routed_forward, 

102 require_attrs, 

103) 

104from hyper_parallel.distributed.recipe_spec import ( 

105 local_compute, 

106) 

107 

108 

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

110# a SUGGESTION shown in preflight error messages and describe_moe_module 

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

112EP_ARCHETYPE_SUGGESTIONS = { 

113 "qwen2moe": "qwen2moe_shared_expert_gate", 

114 "qwen2_moe": "qwen2moe_shared_expert_gate", 

115 "mixtral": "mixtral_topk_router", 

116 "deepseekv3": "deepseekv3_sigmoid_group_shared", 

117 "deepseek_v3": "deepseekv3_sigmoid_group_shared", 

118 "glm4moe": "deepseekv3_sigmoid_group_shared", 

119 "glm4_moe": "deepseekv3_sigmoid_group_shared", 

120} 

121 

122 

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

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

125 if ep_mesh is None: 

126 raise ValueError( 

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

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

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

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

131 ) 

132 return ep_mesh.get_group("ep") 

133 

134 

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

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

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

138 

139 

140def build_ep_compute( 

141 module: Any, 

142 ep_mesh: Any, 

143 *, 

144 router_fn: Callable, 

145 archetype_key: str, 

146 expected_attrs, 

147 combine: Callable, 

148 use_grouped_gemm: bool = False, 

149 fix_router: bool = False, 

150 capacity_factor: Optional[float] = None, 

151 overlap_fn: Optional[Callable] = None, 

152) -> Callable: 

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

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

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

156 that differs between archetypes). 

157 

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

159 branch output and returns the MoE block output. 

160 

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

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

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

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

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

166 receives that value as a fourth argument. 

167 

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

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

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

171 dispatch primitives stay in this generic layer. 

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

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

174 ``router_fn`` names one, else 

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

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

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

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

179 """ 

180 router_fn = resolve_moe_router(router_fn, fix_router) 

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

182 _require_moe_interface(module, expected_attrs, archetype_key) 

183 if use_grouped_gemm: 

184 bind_local_expert_forward( 

185 module, 

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

187 use_grouped_gemm=True, 

188 ) 

189 else: 

190 # Keep the established call contract unchanged for every reference 

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

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

193 

194 if capacity_factor is not None: 

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

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

197 module.experts.capacity_factor = float(capacity_factor) 

198 

199 if overlap_fn is None: 

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

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

202 routed = ep_routed_forward( 

203 module, hidden_states, router_fn=router_fn, ep_group=ep_group) 

204 return combine(module, hidden_states, routed) 

205 else: 

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

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

208 state = ep_routed_dispatch( 

209 module, hidden_states, router_fn=router_fn, ep_group=ep_group) 

210 overlapped = overlap_fn(module, hidden_states) 

211 routed = ep_routed_experts_and_combine(module, state, ep_group) 

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

213 

214 return compute_fn 

215 

216 

217@local_compute 

218def routed_only_ep_compute_fn( 

219 *, 

220 module: Any, 

221 mesh: Any, 

222 tp_mesh: Any, 

223 cp_mesh: Any, 

224 ep_mesh: Any, 

225 fix_router: bool = False, 

226) -> Callable: 

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

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

229 

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

231 

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

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

234 """ 

235 del mesh, tp_mesh, cp_mesh 

236 return build_ep_compute( 

237 module, 

238 ep_mesh, 

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

240 archetype_key="routed_only_softmax_topk", 

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

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

243 ) 

244 

245 

246@local_compute 

247def mixtral_ep_compute_fn( 

248 *, 

249 module: Any, 

250 mesh: Any, 

251 tp_mesh: Any, 

252 cp_mesh: Any, 

253 ep_mesh: Any, 

254 fix_router: bool = False, 

255) -> Callable: 

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

257 

258 Transformers 5.12 Mixtral uses a TopKRouter returning 

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

260 perturbs hidden states before both routing and expert computation. 

261 

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

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

264 """ 

265 del mesh, tp_mesh, cp_mesh 

266 router_fn = resolve_moe_router("mixtral", fix_router) 

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

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

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

270 

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

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

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

274 if module.training and jitter_noise > 0.0: 

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

276 1.0 - jitter_noise, 

277 1.0 + jitter_noise, 

278 ) 

279 hidden_states = hidden_states * noise 

280 return ep_routed_forward( 

281 module, 

282 hidden_states, 

283 router_fn=router_fn, 

284 ep_group=ep_group, 

285 ) 

286 

287 return compute_fn 

288 

289 

290@local_compute 

291def qwen2moe_ep_compute_fn( 

292 *, 

293 module: Any, 

294 mesh: Any, 

295 tp_mesh: Any, 

296 cp_mesh: Any, 

297 ep_mesh: Any, 

298 fix_router: bool = False, 

299) -> Callable: 

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

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

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

303 

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

305 ``shared_expert_gate``. 

306 

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

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

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

310 """ 

311 del mesh, tp_mesh, cp_mesh 

312 

313 def combine( 

314 module: Any, 

315 hidden_states: torch.Tensor, 

316 routed: torch.Tensor, 

317 ) -> torch.Tensor: 

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

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

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

321 return routed + gate * shared 

322 

323 return build_ep_compute( 

324 module, 

325 ep_mesh, 

326 router_fn="qwen2moe", 

327 fix_router=fix_router, 

328 archetype_key="qwen2moe_shared_expert_gate", 

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

330 combine=combine, 

331 ) 

332 

333 

334@local_compute 

335def deepseekv3_ep_compute_fn( 

336 *, 

337 module: Any, 

338 mesh: Any, 

339 tp_mesh: Any, 

340 cp_mesh: Any, 

341 ep_mesh: Any, 

342 use_grouped_gemm: bool = False, 

343 overlap_shared_expert: bool = False, 

344 fix_router: bool = False, 

345 capacity_factor: Optional[float] = None, 

346) -> Callable: 

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

348 routing (with e_score_correction_bias / routed_scaling_factor, already 

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

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

351 

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

353 

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

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

356 

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

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

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

360 

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

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

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

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

365 

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

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

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

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

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

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

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

373 """ 

374 del mesh, tp_mesh, cp_mesh 

375 

376 def combine( 

377 module: Any, 

378 hidden_states: torch.Tensor, 

379 routed: torch.Tensor, 

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

381 ) -> torch.Tensor: 

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

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

384 return routed + shared # nested boundary 

385 

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

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

388 return module.shared_experts(hidden_states) # nested boundary 

389 

390 return build_ep_compute( 

391 module, 

392 ep_mesh, 

393 router_fn="deepseekv3", 

394 fix_router=fix_router, 

395 archetype_key="deepseekv3_sigmoid_group_shared", 

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

397 combine=combine, 

398 use_grouped_gemm=use_grouped_gemm, 

399 capacity_factor=capacity_factor, 

400 overlap_fn=overlap_shared if overlap_shared_expert else None, 

401 ) 

402 

403 

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

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

406# describe/preflight messaging). 

407EP_ARCHETYPES = { 

408 "routed_only_softmax_topk": routed_only_ep_compute_fn, 

409 "mixtral_topk_router": mixtral_ep_compute_fn, 

410 "qwen2moe_shared_expert_gate": qwen2moe_ep_compute_fn, 

411 "deepseekv3_sigmoid_group_shared": deepseekv3_ep_compute_fn, 

412}