Skip to content

Commit faa0f95

Browse files
committed
[KernelGen][thead] Add linalg_eigvals vendor specialization
1 parent d6fd7fe commit faa0f95

2 files changed

Lines changed: 83 additions & 0 deletions

File tree

src/flag_gems/runtime/backend/_thead/ops/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
from .broadcast_to import broadcast_to
1616
from .index_copy_ import index_copy, index_copy_
1717
from .lcm import lcm, lcm_
18+
from .linalg_eigvals import _linalg_eigvals
1819
from .log_normal_ import log_normal_
1920
from .special_chebyshev_polynomial_u import special_chebyshev_polynomial_u
2021

2122
__all__ = [
23+
"_linalg_eigvals",
2224
"adaptive_max_pool3d_backward",
2325
"broadcast_to",
2426
"index_copy",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2026, The FlagOS Contributors.
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+
# Generated by KernelGen: https://github.com/flagos-ai/KernelGen
16+
import logging
17+
18+
import torch
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
def _linalg_eigvals(inp):
24+
"""Compute the eigenvalues of a square matrix.
25+
26+
Thead specialization: the PPU's native CUDA key holds a cuSOLVER-backed
27+
implementation (``cusolverDnXgeev``) that is unsupported on this hardware,
28+
and FlagGems' generic registrar cannot override it (no ``allow_override``).
29+
The generic Python body itself offloads to CPU LAPACK
30+
(``torch.linalg.eigvals(inp.cpu())``), which is correct but is never
31+
reached because dispatch is intercepted by the native cuSOLVER kernel.
32+
33+
This implementation keeps the proven CPU-offload path: eigenvalues are
34+
computed on CPU via LAPACK and moved back to the original device. This
35+
trades PCIe round-trip latency for correctness and device enablement —
36+
a pure-Triton general eigensolver (QR iteration with shifts for
37+
non-symmetric matrices) is out of scope here.
38+
"""
39+
logger.debug(
40+
"GEMS_THEAD _LINALG_EIGVALS, shape: %s, dtype: %s", inp.shape, inp.dtype
41+
)
42+
43+
if inp.ndim < 2 or inp.shape[-2] != inp.shape[-1]:
44+
raise ValueError(
45+
"_linalg_eigvals: input must be a square matrix or batch of square matrices"
46+
)
47+
48+
# cuSOLVER path supports float32/complex64/complex128; keep the same contract.
49+
if inp.dtype not in (torch.float32, torch.complex64, torch.complex128):
50+
raise TypeError(
51+
f"_linalg_eigvals only supports float32/complex64/complex128, got {inp.dtype}"
52+
)
53+
54+
# CPU uses LAPACK (geev), which is supported and accurate.
55+
return torch.linalg.eigvals(inp.cpu()).to(inp.device)
56+
57+
58+
def _dispatched__linalg_eigvals(inp):
59+
"""CUDA-key dispatcher for ``aten::_linalg_eigvals``.
60+
61+
Force-overrides the cuSOLVER-occupied CUDA key: when FlagGems is active
62+
(``use_gems`` context entered, indicated by ``current_work_registrar``) it
63+
routes to the thead specialization above; otherwise it falls back to the
64+
same CPU-LAPACK offload so the op stays usable in eager mode (the native
65+
cuSOLVER kernel is broken on this hardware, so there is no device-side
66+
baseline to preserve).
67+
"""
68+
import flag_gems
69+
70+
if getattr(flag_gems, "current_work_registrar", None) is not None:
71+
return _linalg_eigvals(inp)
72+
# The native CUDA implementation (cusolverDnXgeev) is unsupported on PPU,
73+
# and redispatch to a CUDA-resident keyset just loops back into this
74+
# dispatcher. Fall back to the same CPU-LAPACK path directly.
75+
logger.debug("GEMS_THEAD _LINALG_EIGVALS eager fallback (CPU offload)")
76+
return _linalg_eigvals(inp)
77+
78+
79+
# Persistent registration that force-overrides the cuSOLVER-occupied CUDA key.
80+
_linalg_eigvals_lib = torch.library.Library("aten", "IMPL")
81+
_linalg_eigvals_lib.impl("_linalg_eigvals", _dispatched__linalg_eigvals, "CUDA")

0 commit comments

Comments
 (0)