duckdb-luajit 加矩阵计算,SQL 里做 SVD 特征分解
数据分析绕不开矩阵。
推荐系统要算用户-物品相似度,降维要算协方差,图像处理要算变换,回归要算最小二乘——这些的底层都是同一件事:矩阵分解。SVD、特征值、QR、Cholesky,一串名字,全是线代课本里的老熟人。
可 SQL 里没有这些。DuckDB 的 SQL 再强,SELECT 也算不出一组特征值。以前的办法:导出数据给 Python,调 numpy.linalg,算完再导回来。
我在 duckdb-luajit 里塞了一个 LuaJIT 运行时,SQL 能直接跑 Lua。这次给 libs 仓库加的新成员是 linalg:一个纯 Lua 胶水,FFI 直调系统自带的 OpenBLAS——也就是 LAPACK 的实现。
LAPACK 你可能没听过,但它的用户你天天见。MATLAB、R、numpy 的矩阵分解,底层全是它。40 年的工业标准,Fortran 写的老祖宗,至今没人能替代。
整条链路长这样:
duckdb-luajit linalg 架构
装法还是一样,一条 SQL:
LOAD luajit;
SELECT * FROM luajit_module(mode := 'install', sql_name := 'linalg');
然后矩阵乘法:
SELECT luajit_s('linalg', {'op':'matmul', 'a':[1,2,3,4], 'm':2, 'n':2,
'b':[5,6,7,8], 'mb':2, 'nb':2});
返回:
{"c":[19,22,43,50]}[[1,2],[3,4]] 乘 [[5,6],[7,8]],等于 [[19,22],[43,50]]。手算对得上。
SVD 也一行:
SELECT luajit_s('linalg', {'op':'svd', 'a':[1,2,3,4], 'm':2, 'n':2});
-- → {"s":[5.465,0.366],"u":[...],"vt":[...]}
奇异值 5.465 和 0.366,和 numpy.linalg.svd 逐位一致。
现在有的算子:matmul(乘法)、svd(奇异值分解)、eigh(对称特征值)、inv(求逆)、lu(LU 分解)、chol(Cholesky)、qr(QR 分解)、norm(范数)。够把大多数线代作业在 SQL 里做完了。
验证我做了 10 个锚定用例,全部和手算或解析解对得上。其中有个 3×2 的非方阵 SVD,专门用来验证 U·Σ·Vᵀ 能还原原矩阵——这个用例后来救了我一次。
踩坑最有意思。LAPACK 是 Fortran 写的,矩阵按列主序存,和 C/Python 习惯的行主序正好相反。第一次写 SVD 忘了转,2×2 的测试居然通过了——因为 A 和 Aᵀ 的奇异值恰好相同,转没转结果一样。直到加了 3×2 的非方阵用例,才把这个 bug 炸出来。教训:锚定用例必须覆盖非方阵。
这套东西在 GitHub 上,duckdb-luajit-libs 仓库,install 一条 SQL 就能用。MIT 协议,随便用。
SQL 里做数值计算,地基已经铺好了。矩阵分解有了,下一步 PCA、推荐系统的协同过滤,都能直接在 SQL 里搭。你平时会在 SQL 里算矩阵吗?评论区聊聊。
● ● ●
参考来源
- 1.
LAPACK(Linear Algebra PACKage,40 年工业标准):netlib.org/lapack - 2.
OpenBLAS(LAPACK 的高性能实现,系统自带):github.com/OpenMathLib/OpenBLAS - 3.
duckdb-luajit 扩展:github.com/alitrack/duckdb-luajit - 4.
duckdb-luajit-libs 库仓库(linalg 在此):github.com/alitrack/duckdb-luajit-libs - 5.
numpy.linalg.svd 文档(对拍基准):numpy.org/doc/stable/reference/generated/numpy.linalg.svd.html