NumPy 源码解析(二十七)
.\numpy\numpy\linalg\tests\test_regression.py
import warnings
import pytest
import numpy as np
from numpy import linalg, arange, float64, array, dot, transpose
from numpy.testing import (
assert_, assert_raises, assert_equal, assert_array_equal,
assert_array_almost_equal, assert_array_less
)
class TestRegression:
def test_eig_build(self):
rva = array([1.03221168e+02 + 0.j,
-1.91843603e+01 + 0.j,
-6.04004526e-01 + 15.84422474j,
-6.04004526e-01 - 15.84422474j,
-1.13692929e+01 + 0.j,
-6.57612485e-01 + 10.41755503j,
-6.57612485e-01 - 10.41755503j,
1.82126812e+01 + 0.j,
1.06011014e+01 + 0.j,
7.80732773e+00 + 0.j,
-7.65390898e-01 + 0.j,
1.51971555e-15 + 0.j,
-1.51308713e-15 + 0.j])
a = arange(13 * 13, dtype=float64)
a.shape = (13, 13)
a = a % 17
va, ve = linalg.eig(a)
va.sort()
rva.sort()
assert_array_almost_equal(va, rva)
def test_eigh_build(self):
rvals = [68.60568999, 89.57756725, 106.67185574]
cov = array([[77.70273908, 3.51489954, 15.64602427],
[3.51489954, 88.97013878, -1.07431931],
[15.64602427, -1.07431931, 98.18223512]])
vals, vecs = linalg.eigh(cov)
assert_array_almost_equal(vals, rvals)
def test_svd_build(self):
a = array([[0., 1.], [1., 1.], [2., 1.], [3., 1.]])
m, n = a.shape
u, s, vh = linalg.svd(a)
b = dot(transpose(u[:, n:]), a)
assert_array_almost_equal(b, np.zeros((2, 2)))
def test_norm_vector_badarg(self):
assert_raises(ValueError, linalg.norm, array([1., 2., 3.]), 'fro')
def test_lapack_endian(self):
a = array([[5.7998084, -2.1825367],
[-2.1825367, 9.85910595]], dtype='>f8')
b = array(a, dtype='<f8')
ap = linalg.cholesky(a)
bp = linalg.cholesky(b)
assert_array_equal(ap, bp)
def test_large_svd_32bit(self):
x = np.eye(1000, 66)
np.linalg.svd(x)
def test_svd_no_uv(self):
for shape in (3, 4), (4, 4), (4, 3):
for t in float, complex:
a = np.ones(shape, dtype=t)
w = linalg.svd(a, compute_uv=False)
c = np.count_nonzero(np.absolute(w) > 0.5)
assert_equal(c, 1)
assert_equal(np.linalg.matrix_rank(a), 1)
assert_array_less(1, np.linalg.norm(a, ord=2))
w_svdvals = linalg.svdvals(a)
assert_array_almost_equal(w, w_svdvals)
def test_norm_object_array(self):
testvector = np.array([np.array([0, 1]), 0, 0], dtype=object)
norm = linalg.norm(testvector)
assert_array_equal(norm, [0, 1])
assert_(norm.dtype == np.dtype('float64'))
norm = linalg.norm(testvector, ord=1)
assert_array_equal(norm, [0, 1])
assert_(norm.dtype != np.dtype('float64'))
norm = linalg.norm(testvector, ord=2)
assert_array_equal(norm, [0, 1])
assert_(norm.dtype == np.dtype('float64'))
assert_raises(ValueError, linalg.norm, testvector, ord='fro')
assert_raises(ValueError, linalg.norm, testvector, ord='nuc')
assert_raises(ValueError, linalg.norm, testvector, ord=np.inf)
assert_raises(ValueError, linalg.norm, testvector, ord=-np.inf)
assert_raises(ValueError, linalg.norm, testvector, ord=0)
assert_raises(ValueError, linalg.norm, testvector, ord=-1)
assert_raises(ValueError, linalg.norm, testvector, ord=-2)
testmatrix = np.array([[np.array([0, 1]), 0, 0],
[0, 0, 0]], dtype=object)
norm = linalg.norm(testmatrix)
assert_array_equal(norm, [0, 1])
assert_(norm.dtype == np.dtype('float64'))
norm = linalg.norm(testmatrix, ord='fro')
assert_array_equal(norm, [0, 1])
assert_(norm.dtype == np.dtype('float64'))
assert_raises(TypeError, linalg.norm, testmatrix, ord='nuc')
assert_raises(ValueError, linalg.norm, testmatrix, ord=np.inf)
assert_raises(ValueError, linalg.norm, testmatrix, ord=-np.inf)
assert_raises(ValueError, linalg.norm, testmatrix, ord=0)
assert_raises(ValueError, linalg.norm, testmatrix, ord=1)
assert_raises(ValueError, linalg.norm, testmatrix, ord=-1)
assert_raises(TypeError, linalg.norm, testmatrix, ord=2)
assert_raises(TypeError, linalg.norm, testmatrix, ord=-2)
assert_raises(ValueError, linalg.norm, testmatrix, ord=3)
def test_lstsq_complex_larger_rhs(self):
size = 20
n_rhs = 70
G = np.random.randn(size, size) + 1j * np.random.randn(size, size)
u = np.random.randn(size, n_rhs) + 1j * np.random.randn(size, n_rhs)
b = G.dot(u)
u_lstsq, res, rank, sv = linalg.lstsq(G, b, rcond=None)
assert_array_almost_equal(u_lstsq, u)
@pytest.mark.parametrize("upper", [True, False])
def test_cholesky_empty_array(self, upper):
res = np.linalg.cholesky(np.zeros((0, 0)), upper=upper)
assert res.size == 0
@pytest.mark.parametrize("rtol", [0.0, [0.0] * 4, np.zeros((4,))])
def test_matrix_rank_rtol_argument(self, rtol):
x = np.zeros((4, 3, 2))
res = np.linalg.matrix_rank(x, rtol=rtol)
assert res.shape == (4,)
.\numpy\numpy\linalg\tests\__init__.py
from collections import Counter, defaultdict
def count_words(s):
return Counter(s.split())
def find_anagrams(words):
anagrams = defaultdict(list)
for word in words:
sorted_word = tuple(sorted(word))
anagrams[sorted_word].append(word)
return anagrams
.\numpy\numpy\linalg\umath_linalg.cpp
/*
*****************************************************************************
** INCLUDES **
*****************************************************************************
*/
static const char* umath_linalg_version_string = "0.1.5"; // 定义 umath_linalg 的版本字符串
/*
****************************************************************************
* Debugging support *
****************************************************************************
*/
do { \
fprintf (stderr, \
"%s:%d:%s\n", \
__FILE__, \
__LINE__, \
__FUNCTION__); \
STACK_TRACE; \
} while (0) // 定义输出详细调试信息的宏,包括文件名、行号和函数名
void
dbg_stack_trace() // 调试用的栈跟踪函数
{
void *trace[32];
size_t size;
size = backtrace(trace, sizeof(trace)/sizeof(trace[0])); // 获取当前栈信息
backtrace_symbols_fd(trace, size, 1); // 输出栈信息到文件描述符 1(标准错误)
}
/*
*****************************************************************************
* BLAS/LAPACK calling macros *
*****************************************************************************
*/
typedef CBLAS_INT fortran_int; // 将 CBLAS_INT 定义为 fortran_int 类型
typedef struct { float r, i; } f2c_complex; // 复数结构体定义,单精度
typedef struct { double r, i; } f2c_doublecomplex; // 复数结构体定义,双精度
/* typedef long int (*L_fp)(); */ // 定义一个 long int 类型的函数指针
typedef float fortran_real; // 将 float 定义为 fortran_real 类型
typedef double fortran_doublereal; // 将 double 定义为 fortran_doublereal 类型
typedef f2c_complex fortran_complex; // 将 f2c_complex 定义为 fortran_complex 类型
typedef f2c_doublecomplex fortran_doublecomplex; // 将 f2c_doublecomplex 定义为 fortran_doublecomplex 类型
extern "C" fortran_int
FNAME(sgeev)(char *jobvl, char *jobvr, fortran_int *n,
float a[], fortran_int *lda, float wr[], float wi[],
float vl[], fortran_int *ldvl, float vr[], fortran_int *ldvr,
float work[], fortran_int lwork[],
fortran_int *info); // 声明 sgeev 函数,用于求解特征值问题
extern "C" fortran_int
FNAME(dgeev)(char *jobvl, char *jobvr, fortran_int *n,
double a[], fortran_int *lda, double wr[], double wi[],
double vl[], fortran_int *ldvl, double vr[], fortran_int *ldvr,
double work[], fortran_int lwork[],
fortran_int *info);
extern "C" fortran_int
FNAME(cgeev)(char *jobvl, char *jobvr, fortran_int *n,
f2c_complex a[], fortran_int *lda,
f2c_complex w[],
f2c_complex vl[], fortran_int *ldvl,
f2c_complex vr[], fortran_int *ldvr,
f2c_complex work[], fortran_int *lwork,
float rwork[],
fortran_int *info);
extern "C" fortran_int
FNAME(zgeev)(char *jobvl, char *jobvr, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
f2c_doublecomplex w[],
f2c_doublecomplex vl[], fortran_int *ldvl,
f2c_doublecomplex vr[], fortran_int *ldvr,
f2c_doublecomplex work[], fortran_int *lwork,
double rwork[],
fortran_int *info);
FNAME(dgeev)(char *jobvl, char *jobvr, fortran_int *n,
double a[], fortran_int *lda, double wr[], double wi[],
double vl[], fortran_int *ldvl, double vr[], fortran_int *ldvr,
double work[], fortran_int lwork[],
fortran_int *info);
extern "C" fortran_int
FNAME(cgeev)(char *jobvl, char *jobvr, fortran_int *n,
f2c_complex a[], fortran_int *lda,
f2c_complex w[],
f2c_complex vl[], fortran_int *ldvl,
f2c_complex vr[], fortran_int *ldvr,
f2c_complex work[], fortran_int *lwork,
float rwork[],
fortran_int *info);
extern "C" fortran_int
FNAME(zgeev)(char *jobvl, char *jobvr, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
f2c_doublecomplex w[],
f2c_doublecomplex vl[], fortran_int *ldvl,
f2c_doublecomplex vr[], fortran_int *ldvr,
f2c_doublecomplex work[], fortran_int *lwork,
double rwork[],
fortran_int *info);
FNAME(cgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs,
f2c_complex a[], fortran_int *lda,
f2c_complex b[], fortran_int *ldb,
float s[], float *rcond, fortran_int *rank,
f2c_complex work[], fortran_int *lwork,
float rwork[], fortran_int iwork[],
fortran_int *info);
// 声明一个调用名为 `cgelsd` 的外部函数,该函数用于解决线性最小二乘问题,接受多个参数:
// - m: 矩阵 A 的行数
// - n: 矩阵 A 的列数
// - nrhs: 右侧矩阵 b 的列数
// - a: 复数类型的矩阵 A 的数据数组
// - lda: 矩阵 A 的行跨度
// - b: 复数类型的右侧矩阵 b 的数据数组
// - ldb: 右侧矩阵 b 的行跨度
// - s: 存储奇异值的浮点数数组
// - rcond: 控制奇异值截断的浮点数
// - rank: 返回的有效秩
// - work: 工作区域的复数类型数组
// - lwork: 工作区域的长度
// - rwork: 实数类型的工作区域数组
// - iwork: 整数类型的工作区域数组
// - info: 返回状态信息的整数指针
extern "C" fortran_int
FNAME(zgelsd)(fortran_int *m, fortran_int *n, fortran_int *nrhs,
f2c_doublecomplex a[], fortran_int *lda,
f2c_doublecomplex b[], fortran_int *ldb,
double s[], double *rcond, fortran_int *rank,
f2c_doublecomplex work[], fortran_int *lwork,
double rwork[], fortran_int iwork[],
fortran_int *info);
// 声明一个调用名为 `zgelsd` 的外部函数,功能与 `cgelsd` 类似,但参数中使用了双精度复数和双精度浮点数类型。
// 用于解决复数形式的线性最小二乘问题,接受的参数与 `cgelsd` 相同。
extern "C" fortran_int
FNAME(dgeqrf)(fortran_int *m, fortran_int *n, double a[], fortran_int *lda,
double tau[], double work[],
fortran_int *lwork, fortran_int *info);
// 声明一个调用名为 `dgeqrf` 的外部函数,该函数实现双精度实数类型的 QR 分解,接受多个参数:
// - m: 矩阵 A 的行数
// - n: 矩阵 A 的列数
// - a: 矩阵 A 的数据数组
// - lda: 矩阵 A 的行跨度
// - tau: 存储元素的反射系数的数组
// - work: 工作区域的双精度实数数组
// - lwork: 工作区域的长度
// - info: 返回状态信息的整数指针
extern "C" fortran_int
FNAME(zgeqrf)(fortran_int *m, fortran_int *n, f2c_doublecomplex a[], fortran_int *lda,
f2c_doublecomplex tau[], f2c_doublecomplex work[],
fortran_int *lwork, fortran_int *info);
// 声明一个调用名为 `zgeqrf` 的外部函数,该函数实现双精度复数类型的 QR 分解,接受的参数与 `dgeqrf` 相同。
// 用于解决复数形式的 QR 分解问题。
extern "C" fortran_int
FNAME(dorgqr)(fortran_int *m, fortran_int *n, fortran_int *k, double a[], fortran_int *lda,
double tau[], double work[],
fortran_int *lwork, fortran_int *info);
// 声明一个调用名为 `dorgqr` 的外部函数,该函数用于计算双精度实数类型的 QR 分解后的 Q 矩阵,接受多个参数:
// - m: 矩阵 A 的行数
// - n: 矩阵 A 的列数
// - k: 维度 k
// - a: 存储 QR 分解后的矩阵 Q 的数据数组
// - lda: 矩阵 A 的行跨度
// - tau: 存储元素的反射系数的数组
// - work: 工作区域的双精度实数数组
// - lwork: 工作区域的长度
// - info: 返回状态信息的整数指针
extern "C" fortran_int
FNAME(zungqr)(fortran_int *m, fortran_int *n, fortran_int *k, f2c_doublecomplex a[],
fortran_int *lda, f2c_doublecomplex tau[],
f2c_doublecomplex work[], fortran_int *lwork, fortran_int *info);
// 声明一个调用名为 `zungqr` 的外部函数,该函数用于计算双精度复数类型的 QR 分解后的 Q 矩阵,接受的参数与 `dorgqr` 相同。
// 用于解决复数形式的 QR 分解问题。
extern "C" fortran_int
FNAME(sgesv)(fortran_int *n, fortran_int *nrhs,
float a[], fortran_int *lda,
fortran_int ipiv[],
float b[], fortran_int *ldb,
fortran_int *info);
// 声明一个调用名为 `sgesv` 的外部函数,该函数用于解决单精度实数类型的线性方程组,接受多个参数:
// - n: 矩阵 A 的行数和列数
// - nrhs: 右侧矩阵 b 的列数
// - a: 存储系数矩阵 A 的数据数组
// - lda: 矩阵 A 的行跨度
// - ipiv: 存储主元信息的整数数组
// - b: 存储右侧矩阵 b 的数据数组
// - ldb: 右侧矩阵 b 的行跨度
// - info: 返回状态信息的整数指针
extern "C" fortran_int
FNAME(dgesv)(fortran_int *n, fortran_int *nrhs,
double a[], fortran_int *lda,
fortran_int ipiv[],
double b[], fortran_int *ldb,
fortran_int *info);
// 声明一个调用名为 `dgesv` 的外部函数,该函数用于解决双精度实数类型的线性方程组,接受的参数与 `sgesv` 相同。
// 用于解决实数形式的线性方程组问题。
extern "C" fortran_int
FNAME(cgesv)(fortran_int *n, fortran_int *nrhs,
f2c_complex a[], fortran_int *lda,
fortran_int ipiv[],
f2c_complex b[], fortran_int *ldb,
fortran_int *info);
// 声明一个调用名为 `cgesv` 的外部函数,该函数用于解决单精度复数类型的线性方程组,接受的参数与 `sgesv` 相同。
// 用于解决复数形式的线性方程组问题。
extern "C" fortran_int
FNAME(zgesv)(fortran_int *n, fortran_int *nrhs,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int ipiv[],
f2c_doublecomplex b[], fortran_int *ldb,
fortran_int *info);
// 声明一个调用名为 `zgesv` 的外部函数,该函数用
FNAME(cgetrf)(fortran_int *m, fortran_int *n,
f2c_complex a[], fortran_int *lda,
fortran_int ipiv[],
fortran_int *info);
extern "C" fortran_int
FNAME(zgetrf)(fortran_int *m, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int ipiv[],
fortran_int *info);
FNAME(cgetrf)(fortran_int *m, fortran_int *n,
f2c_complex a[], fortran_int *lda,
fortran_int ipiv[],
fortran_int *info);
extern "C" fortran_int
FNAME(zgetrf)(fortran_int *m, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int ipiv[],
fortran_int *info);
extern "C" fortran_int
FNAME(spotrf)(char *uplo, fortran_int *n,
float a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(dpotrf)(char *uplo, fortran_int *n,
double a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(cpotrf)(char *uplo, fortran_int *n,
f2c_complex a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(zpotrf)(char *uplo, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(spotrf)(char *uplo, fortran_int *n,
float a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(dpotrf)(char *uplo, fortran_int *n,
double a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(cpotrf)(char *uplo, fortran_int *n,
f2c_complex a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(zpotrf)(char *uplo, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int *info);
extern "C" fortran_int
FNAME(sgesdd)(char *jobz, fortran_int *m, fortran_int *n,
float a[], fortran_int *lda, float s[], float u[],
fortran_int *ldu, float vt[], fortran_int *ldvt, float work[],
fortran_int *lwork, fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(dgesdd)(char *jobz, fortran_int *m, fortran_int *n,
double a[], fortran_int *lda, double s[], double u[],
fortran_int *ldu, double vt[], fortran_int *ldvt, double work[],
fortran_int *lwork, fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(cgesdd)(char *jobz, fortran_int *m, fortran_int *n,
f2c_complex a[], fortran_int *lda,
float s[], f2c_complex u[], fortran_int *ldu,
f2c_complex vt[], fortran_int *ldvt,
f2c_complex work[], fortran_int *lwork,
float rwork[], fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(zgesdd)(char *jobz, fortran_int *m, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
double s[], f2c_doublecomplex u[], fortran_int *ldu,
f2c_doublecomplex vt[], fortran_int *ldvt,
f2c_doublecomplex work[], fortran_int *lwork,
double rwork[], fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(sgesdd)(char *jobz, fortran_int *m, fortran_int *n,
float a[], fortran_int *lda, float s[], float u[],
fortran_int *ldu, float vt[], fortran_int *ldvt, float work[],
fortran_int *lwork, fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(dgesdd)(char *jobz, fortran_int *m, fortran_int *n,
double a[], fortran_int *lda, double s[], double u[],
fortran_int *ldu, double vt[], fortran_int *ldvt, double work[],
fortran_int *lwork, fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(cgesdd)(char *jobz, fortran_int *m, fortran_int *n,
f2c_complex a[], fortran_int *lda,
float s[], f2c_complex u[], fortran_int *ldu,
f2c_complex vt[], fortran_int *ldvt,
f2c_complex work[], fortran_int *lwork,
float rwork[], fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(zgesdd)(char *jobz, fortran_int *m, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
double s[], f2c_doublecomplex u[], fortran_int *ldu,
f2c_doublecomplex vt[], fortran_int *ldvt,
f2c_doublecomplex work[], fortran_int *lwork,
double rwork[], fortran_int iwork[], fortran_int *info);
extern "C" fortran_int
FNAME(spotrs)(char *uplo, fortran_int *n, fortran_int *nrhs,
float a[], fortran_int *lda,
float b[], fortran_int *ldb,
fortran_int *info);
extern "C" fortran_int
FNAME(dpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs,
double a[], fortran_int *lda,
double b[], fortran_int *ldb,
fortran_int *info);
extern "C" fortran_int
FNAME(cpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs,
f2c_complex a[], fortran_int *lda,
f2c_complex b[], fortran_int *ldb,
fortran_int *info);
extern "C" fortran_int
FNAME(zpotrs)(char *uplo, fortran_int *n, fortran_int *nrhs,
f2c_doublecomplex a[], fortran_int *lda,
f2c_doublecomplex b[], fortran_int *ldb,
fortran_int *info);
// 声明一个函数 FNAME(zpotrs),用于求解复数域的线性方程组 A * X = B,其中 A 是复数矩阵,B 是复数向量,返回解 X
extern "C" fortran_int
FNAME(spotri)(char *uplo, fortran_int *n,
float a[], fortran_int *lda,
fortran_int *info);
// 声明一个 extern "C" 的函数 FNAME(spotri),用于计算实数域的对称正定矩阵的逆
extern "C" fortran_int
FNAME(dpotri)(char *uplo, fortran_int *n,
double a[], fortran_int *lda,
fortran_int *info);
// 声明一个 extern "C" 的函数 FNAME(dpotri),用于计算实数域的对称正定矩阵的逆
extern "C" fortran_int
FNAME(cpotri)(char *uplo, fortran_int *n,
f2c_complex a[], fortran_int *lda,
fortran_int *info);
// 声明一个 extern "C" 的函数 FNAME(cpotri),用于计算复数域的对称正定矩阵的逆
extern "C" fortran_int
FNAME(zpotri)(char *uplo, fortran_int *n,
f2c_doublecomplex a[], fortran_int *lda,
fortran_int *info);
// 声明一个 extern "C" 的函数 FNAME(zpotri),用于计算复数域的对称正定矩阵的逆
extern "C" fortran_int
FNAME(scopy)(fortran_int *n,
float *sx, fortran_int *incx,
float *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(scopy),用于复制实数数组中的元素到另一个数组
extern "C" fortran_int
FNAME(dcopy)(fortran_int *n,
double *sx, fortran_int *incx,
double *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(dcopy),用于复制双精度数组中的元素到另一个数组
extern "C" fortran_int
FNAME(ccopy)(fortran_int *n,
f2c_complex *sx, fortran_int *incx,
f2c_complex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(ccopy),用于复制复数数组中的元素到另一个数组
extern "C" fortran_int
FNAME(zcopy)(fortran_int *n,
f2c_doublecomplex *sx, fortran_int *incx,
f2c_doublecomplex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(zcopy),用于复制双精度复数数组中的元素到另一个数组
extern "C" float
FNAME(sdot)(fortran_int *n,
float *sx, fortran_int *incx,
float *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(sdot),用于计算实数数组之间的内积
extern "C" double
FNAME(ddot)(fortran_int *n,
double *sx, fortran_int *incx,
double *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(ddot),用于计算双精度数组之间的内积
extern "C" void
FNAME(cdotu)(f2c_complex *ret, fortran_int *n,
f2c_complex *sx, fortran_int *incx,
f2c_complex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(cdotu),用于计算复数数组之间的未共轭内积
extern "C" void
FNAME(zdotu)(f2c_doublecomplex *ret, fortran_int *n,
f2c_doublecomplex *sx, fortran_int *incx,
f2c_doublecomplex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(zdotu),用于计算双精度复数数组之间的未共轭内积
extern "C" void
FNAME(cdotc)(f2c_complex *ret, fortran_int *n,
f2c_complex *sx, fortran_int *incx,
f2c_complex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(cdotc),用于计算复数数组之间的共轭内积
extern "C" void
FNAME(zdotc)(f2c_doublecomplex *ret, fortran_int *n,
f2c_doublecomplex *sx, fortran_int *incx,
f2c_doublecomplex *sy, fortran_int *incy);
// 声明一个 extern "C" 的函数 FNAME(zdotc),用于计算双精度复数数组之间的共轭内积
extern "C" fortran_int
FNAME(sgemm)(char *transa, char *transb,
fortran_int *m, fortran_int *n, fortran_int *k,
float *alpha,
float *a, fortran_int *lda,
float *b, fortran_int *ldb,
float *beta,
float *c, fortran_int *ldc);
// 声明一个 extern "C" 的函数 FNAME(sgemm),用于进行实数矩阵的乘法运算 C = alpha * A * B + beta * C
extern "C" fortran_int
FNAME(dgemm)(char *transa, char *transb,
fortran_int *m, fortran_int *n, fortran_int *k,
double *alpha,
double *a, fortran_int *lda,
double *b, fortran_int *ldb,
double *beta,
double *c, fortran_int *ldc);
// 声明一个 extern "C" 的函数 FNAME(dgemm),用于进行双精度实数矩阵的乘法运算 C = alpha * A * B + beta * C
FNAME(cgemm)(char *transa, char *transb,
fortran_int *m, fortran_int *n, fortran_int *k,
f2c_complex *alpha,
f2c_complex *a, fortran_int *lda,
f2c_complex *b, fortran_int *ldb,
f2c_complex *beta,
f2c_complex *c, fortran_int *ldc);
// 声明一个函数指针 FNAME(cgemm),接受一系列参数,用于进行复杂的矩阵乘法计算
extern "C" fortran_int
FNAME(zgemm)(char *transa, char *transb,
fortran_int *m, fortran_int *n, fortran_int *k,
f2c_doublecomplex *alpha,
f2c_doublecomplex *a, fortran_int *lda,
f2c_doublecomplex *b, fortran_int *ldb,
f2c_doublecomplex *beta,
f2c_doublecomplex *c, fortran_int *ldc);
// 声明一个 extern "C" 的函数 FNAME(zgemm),用于复杂的双精度复数矩阵乘法计算
TRACE_TXT("Calling LAPACK ( "
FNAME(FUNC)
// 宏定义 LAPACK_T(FUNC),用于调用 LAPACK 函数 FUNC,并输出追踪信息到文本
FNAME(FUNC)
// 宏定义 BLAS(FUNC),用于调用 BLAS 函数 FUNC
FNAME(FUNC)
// 宏定义 LAPACK(FUNC),用于调用 LAPACK 函数 FUNC
/*
*****************************************************************************
** Some handy functions **
*****************************************************************************
*/
static inline int
get_fp_invalid_and_clear(void)
{
int status;
status = npy_clear_floatstatus_barrier((char*)&status);
return !!(status & NPY_FPE_INVALID);
}
// 定义静态内联函数 get_fp_invalid_and_clear,用于获取并清除浮点数无效标志位的状态
static inline void
set_fp_invalid_or_clear(int error_occurred)
{
if (error_occurred) {
npy_set_floatstatus_invalid();
}
else {
npy_clear_floatstatus_barrier((char*)&error_occurred);
}
}
// 定义静态内联函数 set_fp_invalid_or_clear,根据错误发生状态设置或清除浮点数无效状态
/*
*****************************************************************************
** Some handy constants **
*****************************************************************************
*/
// 宏定义 UMATH_LINALG_MODULE_NAME,表示数学线性代数模块的名称
template<typename T>
struct numeric_limits;
template<>
struct numeric_limits<float> {
static constexpr float one = 1.0f;
static constexpr float zero = 0.0f;
static constexpr float minus_one = -1.0f;
static const float ninf;
static const float nan;
};
constexpr float numeric_limits<float>::one;
constexpr float numeric_limits<float>::zero;
constexpr float numeric_limits<float>::minus_one;
const float numeric_limits<float>::ninf = -NPY_INFINITYF;
const float numeric_limits<float>::nan = NPY_NANF;
// 定义模板特化结构 numeric_limits<float>,提供 float 类型的常量和特殊值
template<>
struct numeric_limits<double> {
static constexpr double one = 1.0;
static constexpr double zero = 0.0;
static constexpr double minus_one = -1.0;
static const double ninf;
static const double nan;
};
constexpr double numeric_limits<double>::one;
constexpr double numeric_limits<double>::zero;
constexpr double numeric_limits<double>::minus_one;
const double numeric_limits<double>::ninf = -NPY_INFINITY;
const double numeric_limits<double>::nan = NPY_NAN;
// 定义模板特化结构 numeric_limits<double>,提供 double 类型的常量和特殊值
template<>
struct numeric_limits<npy_cfloat> {
static constexpr npy_cfloat one = {1.0f};
static constexpr npy_cfloat zero = {0.0f};
// 定义模板特化结构 numeric_limits<npy_cfloat>,提供 npy_cfloat 类型的常量 one 和 zero
// 定义复数类型的常量,表示负一
static constexpr npy_cfloat minus_one = {-1.0f};
// 定义复数类型的常量,表示负无穷和非数值
static const npy_cfloat ninf;
static const npy_cfloat nan;
// 定义 numeric_limits 结构体的特化版本,为 npy_cfloat 类型
template<>
struct numeric_limits<npy_cfloat> {
// 定义复数类型的常量,表示一、零、负一
static constexpr npy_cfloat one = {1.0f, 0.0f};
static constexpr npy_cfloat zero = {0.0f, 0.0f};
static constexpr npy_cfloat minus_one = {-1.0f, 0.0f};
// 定义复数类型的常量,表示负无穷和非数值
static const npy_cfloat ninf;
static const npy_cfloat nan;
};
// 初始化 numeric_limits 结构体中 npy_cfloat 类型的负无穷常量
const npy_cfloat numeric_limits<npy_cfloat>::ninf = {-NPY_INFINITYF};
// 初始化 numeric_limits 结构体中 npy_cfloat 类型的非数值常量
const npy_cfloat numeric_limits<npy_cfloat>::nan = {NPY_NANF, NPY_NANF};
// 定义 numeric_limits 结构体的特化版本,为 f2c_complex 类型
template<>
struct numeric_limits<f2c_complex> {
// 定义复数类型的常量,表示一、零、负一
static constexpr f2c_complex one = {1.0f, 0.0f};
static constexpr f2c_complex zero = {0.0f, 0.0f};
static constexpr f2c_complex minus_one = {-1.0f, 0.0f};
// 定义复数类型的常量,表示负无穷和非数值
static const f2c_complex ninf;
static const f2c_complex nan;
};
// 初始化 numeric_limits 结构体中 f2c_complex 类型的负无穷常量
const f2c_complex numeric_limits<f2c_complex>::ninf = {-NPY_INFINITYF, 0.0f};
// 初始化 numeric_limits 结构体中 f2c_complex 类型的非数值常量
const f2c_complex numeric_limits<f2c_complex>::nan = {NPY_NANF, NPY_NANF};
// 定义 numeric_limits 结构体的特化版本,为 npy_cdouble 类型
template<>
struct numeric_limits<npy_cdouble> {
// 定义复数类型的常量,表示一、零、负一
static constexpr npy_cdouble one = {1.0};
static constexpr npy_cdouble zero = {0.0};
static constexpr npy_cdouble minus_one = {-1.0};
// 定义复数类型的常量,表示负无穷和非数值
static const npy_cdouble ninf;
static const npy_cdouble nan;
};
// 初始化 numeric_limits 结构体中 npy_cdouble 类型的负无穷常量
const npy_cdouble numeric_limits<npy_cdouble>::ninf = {-NPY_INFINITY};
// 初始化 numeric_limits 结构体中 npy_cdouble 类型的非数值常量
const npy_cdouble numeric_limits<npy_cdouble>::nan = {NPY_NAN, NPY_NAN};
// 定义 numeric_limits 结构体的特化版本,为 f2c_doublecomplex 类型
template<>
struct numeric_limits<f2c_doublecomplex> {
// 定义复数类型的常量,表示一、零、负一
static constexpr f2c_doublecomplex one = {1.0};
static constexpr f2c_doublecomplex zero = {0.0};
static constexpr f2c_doublecomplex minus_one = {-1.0};
// 定义复数类型的常量,表示负无穷和非数值
static const f2c_doublecomplex ninf;
static const f2c_doublecomplex nan;
};
// 初始化 numeric_limits 结构体中 f2c_doublecomplex 类型的负无穷常量
const f2c_doublecomplex numeric_limits<f2c_doublecomplex>::ninf = {-NPY_INFINITY};
// 初始化 numeric_limits 结构体中 f2c_doublecomplex 类型的非数值常量
const f2c_doublecomplex numeric_limits<f2c_doublecomplex>::nan = {NPY_NAN, NPY_NAN};
/*
*****************************************************************************
** Structs used for data rearrangement **
*****************************************************************************
*/
/*
* 这个结构体包含了如何在本地缓冲区中线性化矩阵的信息,以便它可以被 BLAS 函数使用。
* 所有步长都以字节为单位指定,稍后在特定类型的函数中转换为元素。
*
* rows: 矩阵中的行数
* columns: 矩阵中的列数
* row_strides: 连续行之间的字节数
* column_strides: 连续列之间的字节数
* output_lead_dim: BLAS/LAPACK 中的前导维度(以元素为单位)
*/
struct linearize_data
{
npy_intp rows; // 矩阵行数
npy_intp columns; // 矩阵列数
npy_intp row_strides; // 连续行之间的字节数
npy_intp column_strides; // 连续列之间的字节数
npy_intp output_lead_dim; // 输出的前导维度,以元素为单位
};
static inline
linearize_data init_linearize_data_ex(npy_intp rows,
npy_intp columns,
npy_intp row_strides,
npy_intp column_strides,
npy_intp output_lead_dim)
{
return {rows, columns, row_strides, column_strides, output_lead_dim};
}
static inline
linearize_data init_linearize_data(npy_intp rows,
npy_intp columns,
npy_intp row_strides,
npy_intp column_strides)
{
// 初始化 linearize_data 结构体,使用 columns 作为输出的前导维度
return init_linearize_data_ex(
rows, columns, row_strides, column_strides, columns);
}
static inline void
dump_ufunc_object(PyUFuncObject* ufunc)
{
TRACE_TXT("\n\n%s '%s' (%d input(s), %d output(s), %d specialization(s).\n",
ufunc->core_enabled? "generalized ufunc" : "scalar ufunc",
ufunc->name, ufunc->nin, ufunc->nout, ufunc->ntypes);
if (ufunc->core_enabled) {
int arg;
int dim;
TRACE_TXT("\t%s (%d dimension(s) detected).\n",
ufunc->core_signature, ufunc->core_num_dim_ix);
for (arg = 0; arg < ufunc->nargs; arg++){
int * arg_dim_ix = ufunc->core_dim_ixs + ufunc->core_offsets[arg];
TRACE_TXT("\t\targ %d (%s) has %d dimension(s): (",
arg, arg < ufunc->nin? "INPUT" : "OUTPUT",
ufunc->core_num_dims[arg]);
for (dim = 0; dim < ufunc->core_num_dims[arg]; dim ++) {
TRACE_TXT(" %d", arg_dim_ix[dim]);
}
TRACE_TXT(" )\n");
}
}
}
static inline void
dump_linearize_data(const char* name, const linearize_data* params)
{
// 打印 linearize_data 结构体的详细信息,包括名称、行数、列数、行步长和列步长
TRACE_TXT("\n\t%s rows: %zd columns: %zd"\
"\n\t\trow_strides: %td column_strides: %td"\
"\n", name, params->rows, params->columns,
params->row_strides, params->column_strides);
}
static inline void
print(npy_float s)
{
// 打印单精度浮点数 s
TRACE_TXT(" %8.4f", s);
}
static inline void
print(npy_double d)
{
// 打印双精度浮点数 d
TRACE_TXT(" %10.6f", d);
}
static inline void
print(npy_cfloat c)
{
// 打印复数结构 npy_cfloat c 的实部和虚部
float* c_parts = (float*)&c;
TRACE_TXT("(%8.4f, %8.4fj)", c_parts[0], c_parts[1]);
}
static inline void
print(npy_cdouble z)
{
// 打印双精度复数结构 npy_cdouble z 的实部和虚部
double* z_parts = (double*)&z;
TRACE_TXT("(%8.4f, %8.4fj)", z_parts[0], z_parts[1]);
}
/*
*****************************************************************************
** Basics **
*****************************************************************************
*/
// 定义一个静态内联函数,返回两个fortran_int类型数中较小的一个
static inline fortran_int
fortran_int_min(fortran_int x, fortran_int y) {
return x < y ? x : y;
}
// 定义一个静态内联函数,返回两个fortran_int类型数中较大的一个
static inline fortran_int
fortran_int_max(fortran_int x, fortran_int y) {
return x > y ? x : y;
}
// 定义宏INIT_OUTER_LOOP_1,初始化外部循环的第一级
npy_intp dN = *dimensions++;\
npy_intp N_;\
npy_intp s0 = *steps++;
// 定义宏INIT_OUTER_LOOP_2,初始化外部循环的第二级,继承INIT_OUTER_LOOP_1
INIT_OUTER_LOOP_1\
npy_intp s1 = *steps++;
// 定义宏INIT_OUTER_LOOP_3,初始化外部循环的第三级,继承INIT_OUTER_LOOP_2
INIT_OUTER_LOOP_2\
npy_intp s2 = *steps++;
// 定义宏INIT_OUTER_LOOP_4,初始化外部循环的第四级,继承INIT_OUTER_LOOP_3
INIT_OUTER_LOOP_3\
npy_intp s3 = *steps++;
// 定义宏INIT_OUTER_LOOP_5,初始化外部循环的第五级,继承INIT_OUTER_LOOP_4
INIT_OUTER_LOOP_4\
npy_intp s4 = *steps++;
// 定义宏INIT_OUTER_LOOP_6,初始化外部循环的第六级,继承INIT_OUTER_LOOP_5
INIT_OUTER_LOOP_5\
npy_intp s5 = *steps++;
// 定义宏INIT_OUTER_LOOP_7,初始化外部循环的第七级,继承INIT_OUTER_LOOP_6
INIT_OUTER_LOOP_6\
npy_intp s6 = *steps++;
// 定义宏BEGIN_OUTER_LOOP_2,开始外部循环的第二级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1) {
// 定义宏BEGIN_OUTER_LOOP_3,开始外部循环的第三级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1,\
args[2] += s2) {
// 定义宏BEGIN_OUTER_LOOP_4,开始外部循环的第四级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1,\
args[2] += s2,\
args[3] += s3) {
// 定义宏BEGIN_OUTER_LOOP_5,开始外部循环的第五级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1,\
args[2] += s2,\
args[3] += s3,\
args[4] += s4) {
// 定义宏BEGIN_OUTER_LOOP_6,开始外部循环的第六级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1,\
args[2] += s2,\
args[3] += s3,\
args[4] += s4,\
args[5] += s5) {
// 定义宏BEGIN_OUTER_LOOP_7,开始外部循环的第七级
for (N_ = 0;\
N_ < dN;\
N_++, args[0] += s0,\
args[1] += s1,\
args[2] += s2,\
args[3] += s3,\
args[4] += s4,\
args[5] += s5,\
args[6] += s6) {
// 定义宏END_OUTER_LOOP,结束外部循环
// 定义静态内联函数,更新指针数组中的指针,使其增加偏移量offsets中对应的值
static inline void
update_pointers(npy_uint8** bases, ptrdiff_t* offsets, size_t count)
{
size_t i;
// 遍历指针数组,更新每个指针的偏移量
for (i = 0; i < count; ++i) {
bases[i] += offsets[i];
}
}
/*
*****************************************************************************
** DISPATCHER FUNCS **
*****************************************************************************
*/
// 定义一个静态函数copy,用于复制浮点数数组(单精度)
static fortran_int copy(fortran_int *n,
float *sx, fortran_int *incx,
float *sy, fortran_int *incy) { return FNAME(scopy)(n, sx, incx,
sy, incy);
}
// 定义一个静态函数copy,用于复制浮点数数组(双精度)
static fortran_int copy(fortran_int *n,
double *sx, fortran_int *incx,
double *sy, fortran_int *incy) { return FNAME(dcopy)(n, sx, incx,
sy, incy);
}
// 定义一个静态函数copy,用于复制复数数组(单精度复数)
static fortran_int copy(fortran_int *n,
f2c_complex *sx, fortran_int *incx,
f2c_complex *sy, fortran_int *incy) { return FNAME(ccopy)(n, sx, incx,
sy, incy);
}
// 定义一个静态函数copy,用于复制复数数组(双精度复数)
static fortran_int copy(fortran_int *n,
f2c_doublecomplex *sx, fortran_int *incx,
f2c_doublecomplex *sy, fortran_int *incy) { return FNAME(zcopy)(n, sx, incx,
sy, incy);
}
// 定义一个静态函数getrf,用于调用 LAPACK 库中的单精度 LU 分解函数
static fortran_int getrf(fortran_int *m, fortran_int *n, float a[], fortran_int
*lda, fortran_int ipiv[], fortran_int *info) {
return LAPACK(sgetrf)(m, n, a, lda, ipiv, info);
}
// 定义一个静态函数getrf,用于调用 LAPACK 库中的双精度 LU 分解函数
static fortran_int getrf(fortran_int *m, fortran_int *n, double a[], fortran_int
*lda, fortran_int ipiv[], fortran_int *info) {
return LAPACK(dgetrf)(m, n, a, lda, ipiv, info);
}
// 定义一个静态函数getrf,用于调用 LAPACK 库中的单精度复数 LU 分解函数
static fortran_int getrf(fortran_int *m, fortran_int *n, f2c_complex a[], fortran_int
*lda, fortran_int ipiv[], fortran_int *info) {
return LAPACK(cgetrf)(m, n, a, lda, ipiv, info);
}
// 定义一个静态函数getrf,用于调用 LAPACK 库中的双精度复数 LU 分解函数
static fortran_int getrf(fortran_int *m, fortran_int *n, f2c_doublecomplex a[], fortran_int
*lda, fortran_int ipiv[], fortran_int *info) {
return LAPACK(zgetrf)(m, n, a, lda, ipiv, info);
}
/*
*****************************************************************************
** HELPER FUNCS **
*****************************************************************************
*/
// 定义模板结构fortran_type,用于确定 C 类型对应的 Fortran 类型
template<typename T>
struct fortran_type {
using type = T;
};
// 部分特化,将numpy单精度复数类型映射到双精度复数类型
template<> struct fortran_type<npy_cfloat> { using type = f2c_complex;};
// 部分特化,将numpy双精度复数类型映射到双精度复数类型
template<> struct fortran_type<npy_cdouble> { using type = f2c_doublecomplex;};
// 使用类型别名,简化使用
template<typename T>
using fortran_type_t = typename fortran_type<T>::type;
// 定义模板结构basetype,用于确定 C 类型对应的基本类型
template<typename T>
struct basetype {
using type = T;
};
// 部分特化,将numpy单精度复数类型映射到单精度浮点数
template<> struct basetype<npy_cfloat> { using type = npy_float;};
// 部分特化,将numpy双精度复数类型映射到双精度浮点数
template<> struct basetype<npy_cdouble> { using type = npy_double;};
// 部分特化,将单精度复数类型映射到Fortran实数类型
template<> struct basetype<f2c_complex> { using type = fortran_real;};
// 部分特化,将双精度复数类型映射到Fortran双精度实数类型
template<> struct basetype<f2c_doublecomplex> { using type = fortran_doublereal;};
// 使用类型别名,简化使用
template<typename T>
using basetype_t = typename basetype<T>::type;
// 定义标记结构体scalar_trait,表示标量类型
struct scalar_trait {};
// 定义标记结构体complex_trait,表示复数类型
struct complex_trait {};
// 使用模板条件语句,根据类型大小判断是标量还是复数
template<typename typ>
using dispatch_scalar = typename std::conditional<sizeof(basetype_t<typ>) == sizeof(typ), scalar_trait, complex_trait>::type;
/* rearranging of 2D matrices using blas */
// 定义模板函数dispatch_scalar,用于重新排列二维矩阵,使用BLAS库
template<typename typ>
static inline void *
// 将矩阵展平为一维数组,返回展平后的目标数组指针
linearize_matrix(typ *dst,
typ *src,
const linearize_data* data)
{
// 使用模板定义Fortran类型ftyp为typ类型对应的Fortran类型
using ftyp = fortran_type_t<typ>;
// 检查目标数组是否非空
if (dst) {
int i, j;
// rv指向目标数组的起始地址
typ* rv = dst;
// 将列数转换为Fortran整型
fortran_int columns = (fortran_int)data->columns;
// 将列步长转换为Fortran整型(以typ类型为单位)
fortran_int column_strides =
(fortran_int)(data->column_strides/sizeof(typ));
// Fortran中常用的步长为1
fortran_int one = 1;
// 遍历矩阵的每一行
for (i = 0; i < data->rows; i++) {
// 如果列步长大于0,调用copy函数复制数据
if (column_strides > 0) {
copy(&columns,
(ftyp*)src, &column_strides,
(ftyp*)dst, &one);
}
// 如果列步长小于0,从矩阵末尾开始复制数据
else if (column_strides < 0) {
copy(&columns,
((ftyp*)src + (columns-1)*column_strides),
&column_strides,
(ftyp*)dst, &one);
}
// 如果列步长为0,手动复制每一列数据
else {
/*
* 零步长在某些BLAS实现(如OSX Accelerate)中具有未定义的行为,因此手动处理
*/
for (j = 0; j < columns; ++j) {
memcpy(dst + j, src, sizeof(typ));
}
}
// 更新源数据指针到下一行起始位置
src += data->row_strides/sizeof(typ);
// 更新目标数据指针到下一行起始位置
dst += data->output_lead_dim;
}
// 返回目标数组的起始地址
return rv;
} else {
// 如果目标数组为空,直接返回源数据的起始地址
return src;
}
}
// 将一维数组反展平为矩阵,返回反展平后的源数组指针
template<typename typ>
static inline void *
delinearize_matrix(typ *dst,
typ *src,
const linearize_data* data)
{
// 使用模板定义Fortran类型ftyp为typ类型对应的Fortran类型
using ftyp = fortran_type_t<typ>;
// 检查源数组是否非空
if (src) {
int i;
// rv指向源数组的起始地址
typ *rv = src;
// 将列数转换为Fortran整型
fortran_int columns = (fortran_int)data->columns;
// 将列步长转换为Fortran整型(以typ类型为单位)
fortran_int column_strides =
(fortran_int)(data->column_strides/sizeof(typ));
// Fortran中常用的步长为1
fortran_int one = 1;
// 遍历矩阵的每一行
for (i = 0; i < data->rows; i++) {
// 如果列步长大于0,调用copy函数反复制数据
if (column_strides > 0) {
copy(&columns,
(ftyp*)src, &one,
(ftyp*)dst, &column_strides);
}
// 如果列步长小于0,从矩阵末尾开始反复制数据
else if (column_strides < 0) {
copy(&columns,
(ftyp*)src, &one,
((ftyp*)dst + (columns-1)*column_strides),
&column_strides);
}
// 如果列步长为0,手动反复制每一列数据
else {
/*
* 零步长在某些BLAS实现(如OSX Accelerate)中具有未定义的行为,因此手动处理
*/
if (columns > 0) {
memcpy(dst,
src + (columns-1),
sizeof(typ));
}
}
// 更新源数据指针到下一行起始位置
src += data->output_lead_dim;
// 更新目标数据指针到下一行起始位置
dst += data->row_strides/sizeof(typ);
}
// 返回源数组的起始地址
return rv;
} else {
// 如果源数组为空,直接返回源数组的起始地址
return src;
}
}
template<typename typ>
static inline void
nan_matrix(typ *dst, const linearize_data* data)
{
int i, j;
for (i = 0; i < data->rows; i++) {
typ *cp = dst;
ptrdiff_t cs = data->column_strides/sizeof(typ);
for (j = 0; j < data->columns; ++j) {
*cp = numeric_limits<typ>::nan;
cp += cs;
}
dst += data->row_strides/sizeof(typ);
}
}
template<typename typ>
static inline void
zero_matrix(typ *dst, const linearize_data* data)
{
int i, j;
// 遍历矩阵的每一行
for (i = 0; i < data->rows; i++) {
typ *cp = dst;
// 计算列步长(单位为 typ 类型大小)
ptrdiff_t cs = data->column_strides/sizeof(typ);
// 遍历矩阵的每一列
for (j = 0; j < data->columns; ++j) {
// 将目标矩阵位置清零
*cp = numeric_limits<typ>::zero;
cp += cs; // 移动到下一列的位置
}
dst += data->row_strides/sizeof(typ); // 移动到下一行的位置
}
}
/* identity square matrix generation */
template<typename typ>
static inline void
identity_matrix(typ *matrix, size_t n)
{
size_t i;
// 将矩阵初始化为 n×n 的零矩阵
memset((void *)matrix, 0, n*n*sizeof(typ));
// 遍历矩阵的对角线元素
for (i = 0; i < n; ++i)
{
// 设置对角线上的元素为 1
*matrix = numeric_limits<typ>::one;
matrix += n+1; // 移动到下一个对角线元素的位置
}
}
/* -------------------------------------------------------------------------- */
/* Determinants */
// 返回单精度浮点数的自然对数
static npy_float npylog(npy_float f) { return npy_logf(f);}
// 返回双精度浮点数的自然对数
static npy_double npylog(npy_double d) { return npy_log(d);}
// 返回单精度浮点数的自然指数
static npy_float npyexp(npy_float f) { return npy_expf(f);}
// 返回双精度浮点数的自然指数
static npy_double npyexp(npy_double d) { return npy_exp(d);}
template<typename typ>
static inline void
slogdet_from_factored_diagonal(typ* src,
fortran_int m,
typ *sign,
typ *logdet)
{
typ acc_sign = *sign;
typ acc_logdet = numeric_limits<typ>::zero;
int i;
// 遍历对角线因子的数组
for (i = 0; i < m; i++) {
typ abs_element = *src;
// 如果元素小于零,调整符号并取绝对值
if (abs_element < numeric_limits<typ>::zero) {
acc_sign = -acc_sign;
abs_element = -abs_element;
}
// 累加元素的自然对数
acc_logdet += npylog(abs_element);
src += m+1; // 移动到下一个对角线元素的位置
}
// 存储计算出的行列式的符号和对数绝对值
*sign = acc_sign;
*logdet = acc_logdet;
}
template<typename typ>
static inline typ
det_from_slogdet(typ sign, typ logdet)
{
// 计算行列式的值
typ result = sign * npyexp(logdet);
return result;
}
npy_float npyabs(npy_cfloat z) { return npy_cabsf(z);}
npy_double npyabs(npy_cdouble z) { return npy_cabs(z);}
// 返回复数的实部(单精度浮点数)
inline float RE(npy_cfloat *c) { return npy_crealf(*c); }
// 返回复数的实部(双精度浮点数)
inline double RE(npy_cdouble *c) { return npy_creal(*c); }
// 返回复数的实部(长双精度浮点数)
inline longdouble_t RE(npy_clongdouble *c) { return npy_creall(*c); }
// 返回复数的虚部(单精度浮点数)
inline float IM(npy_cfloat *c) { return npy_cimagf(*c); }
// 返回复数的虚部(双精度浮点数)
inline double IM(npy_cdouble *c) { return npy_cimag(*c); }
// 返回复数的虚部(长双精度浮点数)
inline longdouble_t IM(npy_clongdouble *c) { return npy_cimagl(*c); }
// 设置复数的实部(单精度浮点数)
inline void SETRE(npy_cfloat *c, float real) { npy_csetrealf(c, real); }
// 设置复数的实部(双精度浮点数)
inline void SETRE(npy_cdouble *c, double real) { npy_csetreal(c, real); }
// 设置复数的实部(长双精度浮点数)
inline void SETRE(npy_clongdouble *c, double real) { npy_csetreall(c, real); }
// 设置复数的虚部(单精度浮点数)
inline void SETIM(npy_cfloat *c, float real) { npy_csetimagf(c, real); }
/* 定义一个宏 SETIM,用于设置复数结构中的虚部 */
inline void SETIM(npy_cdouble *c, double real) { npy_csetimag(c, real); }
/* 如果长双精度复数和双精度复数大小不同,定义另一个 SETIM 宏 */
inline void SETIM(npy_clongdouble *c, double real) { npy_csetimagl(c, real); }
/* 定义一个模板函数 mult,用于复数乘法运算 */
template<typename typ>
static inline typ
mult(typ op1, typ op2)
{
typ rv; // 定义结果变量
// 计算复数乘法的实部和虚部
SETRE(&rv, RE(&op1)*RE(&op2) - IM(&op1)*IM(&op2));
SETIM(&rv, RE(&op1)*IM(&op2) + IM(&op1)*RE(&op2));
return rv; // 返回乘积结果
}
/* 定义一个模板函数 slogdet_from_factored_diagonal,用于计算对角线因式分解的 slogdet */
template<typename typ, typename basetyp>
static inline void
slogdet_from_factored_diagonal(typ* src,
fortran_int m,
typ *sign,
basetyp *logdet)
{
int i;
typ sign_acc = *sign; // 初始化累计的符号
basetyp logdet_acc = numeric_limits<basetyp>::zero; // 初始化累计的对数行列式值
for (i = 0; i < m; i++)
{
basetyp abs_element = npyabs(*src); // 计算当前元素的绝对值
typ sign_element;
// 计算当前元素的复数符号
SETRE(&sign_element, RE(src) / abs_element);
SETIM(&sign_element, IM(src) / abs_element);
sign_acc = mult(sign_acc, sign_element); // 累乘符号元素
logdet_acc += npylog(abs_element); // 累加对数行列式值
src += m + 1; // 移动到下一个对角线元素
}
*sign = sign_acc; // 更新总符号
*logdet = logdet_acc; // 更新总对数行列式值
}
/* 定义一个模板函数 det_from_slogdet,用于根据 slogdet 计算行列式值 */
template<typename typ, typename basetyp>
static inline typ
det_from_slogdet(typ sign, basetyp logdet)
{
typ tmp;
SETRE(&tmp, npyexp(logdet)); // 计算指数化的对数行列式值的实部
SETIM(&tmp, numeric_limits<basetyp>::zero); // 设置虚部为零
return mult(sign, tmp); // 返回最终行列式值
}
/* 在 linalg 包中,使用 LAPACK 计算 LU 分解得到行列式 */
template<typename typ, typename basetyp>
static inline void
slogdet_single_element(fortran_int m,
typ* src,
fortran_int* pivots,
typ *sign,
basetyp *logdet)
{
using ftyp = fortran_type_t<typ>; // 定义 Fortran 类型别名
fortran_int info = 0;
fortran_int lda = fortran_int_max(m, 1); // 计算 lda,确保大于等于 m 的最小整数
int i;
/* 注意:原地操作 */
getrf(&m, &m, (ftyp*)src, &lda, pivots, &info); // 在 src 上进行 LU 分解
if (info == 0) {
int change_sign = 0;
/* 注意:Fortran 使用基于 1 的索引 */
// 计算置换过程中改变符号的次数
for (i = 0; i < m; i++)
{
change_sign += (pivots[i] != (i+1));
}
// 根据符号改变次数设置整体符号
*sign = (change_sign % 2)?numeric_limits<typ>::minus_one:numeric_limits<typ>::one;
// 计算对角线因式分解的 slogdet
slogdet_from_factored_diagonal(src, m, sign, logdet);
} else {
/*
如果 getrf 失败,使用 0 作为符号和 -inf 作为 logdet
*/
*sign = numeric_limits<typ>::zero;
*logdet = numeric_limits<basetyp>::ninf;
}
}
/* 定义 slogdet 函数,计算行列式的符号和对数行列式值 */
template<typename typ, typename basetyp>
static void
slogdet(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *NPY_UNUSED(func))
{
fortran_int m;
char *tmp_buff = NULL;
size_t matrix_size;
size_t pivot_size;
size_t safe_m;
/* notes:
* matrix will need to be copied always, as factorization in lapack is
* made inplace
* matrix will need to be in column-major order, as expected by lapack
* code (fortran)
* always a square matrix
* need to allocate memory for both, matrix_buffer and pivot buffer
*/
/* 初始化外部循环 3 */
INIT_OUTER_LOOP_3
/* 将 dimensions[0] 赋给 m,并确保 m 是 size_t 类型 */
m = (fortran_int) dimensions[0];
/* 避免空的 malloc(缓冲区可能未使用),并确保 safe_m 是非零的 */
safe_m = m != 0 ? m : 1;
/* 计算矩阵和 pivot 缓冲区的总大小 */
matrix_size = safe_m * safe_m * sizeof(typ);
pivot_size = safe_m * sizeof(fortran_int);
/* 分配足够的内存给 tmp_buff,包括矩阵和 pivot 缓冲区 */
tmp_buff = (char *)malloc(matrix_size + pivot_size);
if (tmp_buff) {
/* 为了按照 FORTRAN 的顺序获取矩阵,交换了这些步骤 */
/* 初始化线性化数据结构 */
linearize_data lin_data = init_linearize_data(m, m, steps[1], steps[0]);
/* 开始外部循环 3 */
BEGIN_OUTER_LOOP_3
/* 将 args[0] 的数据线性化为 tmp_buff */
linearize_matrix((typ*)tmp_buff, (typ*)args[0], &lin_data);
/* 计算矩阵的 slogdet(行列式的对数) */
slogdet_single_element(m,
(typ*)tmp_buff,
(fortran_int*)(tmp_buff+matrix_size),
(typ*)args[1],
(basetyp*)args[2]);
/* 结束外部循环 */
END_OUTER_LOOP
/* 释放 tmp_buff 的内存 */
free(tmp_buff);
}
else {
/* 如果分配内存失败,则设置内存错误并禁用 C API */
/* TODO: 需要使用新的 ufunc API 来指示错误返回 */
NPY_ALLOW_C_API_DEF
NPY_ALLOW_C_API;
PyErr_NoMemory();
NPY_DISABLE_C_API;
}
}
template<typename typ, typename basetyp>
static void
det(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *NPY_UNUSED(func))
{
fortran_int m;
char *tmp_buff;
size_t matrix_size;
size_t pivot_size;
size_t safe_m;
/* notes:
* matrix will need to be copied always, as factorization in lapack is
* made inplace
* matrix will need to be in column-major order, as expected by lapack
* code (fortran)
* always a square matrix
* need to allocate memory for both, matrix_buffer and pivot buffer
*/
INIT_OUTER_LOOP_2
// 从dimensions数组中获取矩阵的维度并转换为fortran_int类型
m = (fortran_int) dimensions[0];
/* 避免空的malloc(缓冲区可能未使用),确保m是`size_t` */
safe_m = m != 0 ? m : 1;
// 计算需要分配的内存空间大小,用于存储矩阵数据和主元数据
matrix_size = safe_m * safe_m * sizeof(typ);
pivot_size = safe_m * sizeof(fortran_int);
// 分配内存空间,用于临时缓冲区,存储矩阵和主元数据
tmp_buff = (char *)malloc(matrix_size + pivot_size);
if (tmp_buff) {
/* swapped steps to get matrix in FORTRAN order */
// 初始化线性化数据结构,用于将数据转换为FORTRAN顺序的矩阵
linearize_data lin_data = init_linearize_data(m, m, steps[1], steps[0]);
typ sign;
basetyp logdet;
BEGIN_OUTER_LOOP_2
// 线性化输入数据,使其符合FORTRAN顺序
linearize_matrix((typ*)tmp_buff, (typ*)args[0], &lin_data);
// 计算矩阵的行列式的符号和对数行列式
slogdet_single_element(m,
(typ*)tmp_buff,
(fortran_int*)(tmp_buff + matrix_size),
&sign,
&logdet);
// 将计算得到的行列式结果存储在输出参数中
*(typ *)args[1] = det_from_slogdet(sign, logdet);
END_OUTER_LOOP
free(tmp_buff);
}
else {
/* TODO: Requires use of new ufunc API to indicate error return */
// 如果内存分配失败,则发出内存错误异常
NPY_ALLOW_C_API_DEF
NPY_ALLOW_C_API;
PyErr_NoMemory();
NPY_DISABLE_C_API;
}
}
/* -------------------------------------------------------------------------- */
/* Eigh family */
template<typename typ>
struct EIGH_PARAMS_t {
typ *A; /* matrix */
basetype_t<typ> *W; /* eigenvalue vector */
typ *WORK; /* main work buffer */
basetype_t<typ> *RWORK; /* secondary work buffer (for complex versions) */
fortran_int *IWORK;
fortran_int N;
fortran_int LWORK;
fortran_int LRWORK;
fortran_int LIWORK;
char JOBZ;
char UPLO;
fortran_int LDA;
} ;
static inline fortran_int
call_evd(EIGH_PARAMS_t<npy_float> *params)
{
fortran_int rv;
// 调用LAPACK库中的ssyevd函数进行对称矩阵特征值分解
LAPACK(ssyevd)(¶ms->JOBZ, ¶ms->UPLO, ¶ms->N,
params->A, ¶ms->LDA, params->W,
params->WORK, ¶ms->LWORK,
params->IWORK, ¶ms->LIWORK,
&rv);
return rv;
}
static inline fortran_int
call_evd(EIGH_PARAMS_t<npy_double> *params)
{
fortran_int rv;
// 调用LAPACK库中的dsyevd函数进行对称矩阵特征值分解
LAPACK(dsyevd)(¶ms->JOBZ, ¶ms->UPLO, ¶ms->N,
params->A, ¶ms->LDA, params->W,
params->WORK, ¶ms->LWORK,
params->IWORK, ¶ms->LIWORK,
&rv);
return rv;
}
// 调用 LAPACK 库中的 dsyevd 函数来计算对称矩阵的特征值和(可选)特征向量
// 参数说明:
// - params->JOBZ: 用于指定是否计算特征向量的标志
// - params->UPLO: 指定矩阵存储的上/下三角部分
// - params->N: 矩阵的阶数(维度)
// - params->A: 指向存储矩阵数据的数组的指针
// - ¶ms->LDA: 指定矩阵 A 的第一个维度(列数)
// - params->W: 用于存储计算得到的特征值的数组
// - params->WORK: 提供给 LAPACK 函数用于工作空间的数组
// - ¶ms->LWORK: 指定工作空间数组的长度
// - params->IWORK: 提供给 LAPACK 函数用于整数工作空间的数组
// - ¶ms->LIWORK: 指定整数工作空间数组的长度
// - &rv: 返回值,指示函数调用的执行情况(通常是一个整数)
// 返回 rv 变量的值作为函数的返回值
return rv;
/*
* Initialize the parameters to use in for the lapack function _syevd
* Handles buffer allocation
*/
template<typename typ>
static inline int
init_evd(EIGH_PARAMS_t<typ>* params, char JOBZ, char UPLO,
fortran_int N, scalar_trait)
{
npy_uint8 *mem_buff = NULL; // 声明指向无符号字节的指针,用于内存缓冲区
npy_uint8 *mem_buff2 = NULL; // 声明指向无符号字节的指针,用于第二个内存缓冲区
fortran_int lwork; // 定义用于工作区大小的变量
fortran_int liwork; // 定义用于整数工作区大小的变量
npy_uint8 *a, *w, *work, *iwork; // 声明指向无符号字节的指针,用于矩阵A,特征值W,工作区WORK和整数工作区IWORK
size_t safe_N = N; // 安全的N值,用于分配内存大小
size_t alloc_size = safe_N * (safe_N + 1) * sizeof(typ); // 计算分配的内存大小
fortran_int lda = fortran_int_max(N, 1); // 计算LDA,确保大于等于N
mem_buff = (npy_uint8 *)malloc(alloc_size); // 分配内存缓冲区
if (!mem_buff) { // 检查内存分配是否成功
goto error; // 如果分配失败,跳转到错误处理
}
a = mem_buff; // 设置矩阵A的指针
w = mem_buff + safe_N * safe_N * sizeof(typ); // 设置特征值W的指针
params->A = (typ*)a; // 将分配的内存地址赋给参数结构体中的矩阵A
params->W = (typ*)w; // 将分配的内存地址赋给参数结构体中的特征值W
params->RWORK = NULL; /* unused */ // 参数结构体中的RWORK未使用
params->N = N; // 设置参数结构体中的N
params->LRWORK = 0; /* unused */ // 参数结构体中的LRWORK未使用
params->JOBZ = JOBZ; // 设置参数结构体中的JOBZ
params->UPLO = UPLO; // 设置参数结构体中的UPLO
params->LDA = lda; // 设置参数结构体中的LDA
/* Work size query */
{
typ query_work_size; // 用于存储工作区大小的变量
fortran_int query_iwork_size; // 用于存储整数工作区大小的变量
params->LWORK = -1; // 设置LWORK为-1进行工作区大小查询
params->LIWORK = -1; // 设置LIWORK为-1进行整数工作区大小查询
params->WORK = &query_work_size; // 设置WORK指向query_work_size变量的地址
params->IWORK = &query_iwork_size; // 设置IWORK指向query_iwork_size变量的地址
if (call_evd(params) != 0) { // 调用evd函数进行工作区大小查询,并检查返回值
goto error; // 如果查询失败,跳转到错误处理
}
lwork = (fortran_int)query_work_size; // 将查询得到的工作区大小赋给lwork变量
liwork = query_iwork_size; // 将查询得到的整数工作区大小赋给liwork变量
}
mem_buff2 = (npy_uint8 *)malloc(lwork*sizeof(typ) + liwork*sizeof(fortran_int)); // 根据查询得到的工作区大小和整数工作区大小分配第二个内存缓冲区
if (!mem_buff2) { // 检查第二个内存分配是否成功
goto error; // 如果分配失败,跳转到错误处理
}
work = mem_buff2; // 设置工作区WORK的指针
iwork = mem_buff2 + lwork*sizeof(typ); // 设置整数工作区IWORK的指针
params->LWORK = lwork; // 设置参数结构体中的LWORK
params->WORK = (typ*)work; // 将分配的工作区内存地址赋给参数结构体中的WORK
params->LIWORK = liwork; // 设置参数结构体中的LIWORK
params->IWORK = (fortran_int*)iwork; // 将分配的整数工作区内存地址赋给参数结构体中的IWORK
return 1; // 初始化成功,返回1
error:
/* something failed */
memset(params, 0, sizeof(*params)); // 初始化参数结构体为0
free(mem_buff2); // 释放第二个内存缓冲区
free(mem_buff); // 释放第一个内存缓冲区
return 0; // 返回初始化失败
}
static inline fortran_int
call_evd(EIGH_PARAMS_t<npy_cfloat> *params)
{
fortran_int rv; // 定义返回值变量
LAPACK(cheevd)(¶ms->JOBZ, ¶ms->UPLO, ¶ms->N,
(fortran_type_t<npy_cfloat>*)params->A, ¶ms->LDA, params->W,
(fortran_type_t<npy_cfloat>*)params->WORK, ¶ms->LWORK,
params->RWORK, ¶ms->LRWORK,
params->IWORK, ¶ms->LIWORK,
&rv); // 调用LAPACK库中的cheevd函数进行复数浮点数的特征值计算
return rv; // 返回函数调用的结果值
}
static inline fortran_int
call_evd(EIGH_PARAMS_t<npy_cdouble> *params)
{
fortran_int rv; // 定义返回值变量
LAPACK(zheevd)(¶ms->JOBZ, ¶ms->UPLO, ¶ms->N,
(fortran_type_t<npy_cdouble>*)params->A, ¶ms->LDA, params->W,
(fortran_type_t<npy_cdouble>*)params->WORK, ¶ms->LWORK,
params->RWORK, ¶ms->LRWORK,
params->IWORK, ¶ms->LIWORK,
&rv); // 调用LAPACK库中的zheevd函数进行复数双精度浮点数的特征值计算
return rv; // 返回函数调用的结果值
}
template<typename typ>
static inline int
init_evd(EIGH_PARAMS_t<typ> *params,
char JOBZ,
char UPLO,
fortran_int N, complex_trait)
{
using basetyp = basetype_t<typ>;
using ftyp = fortran_type_t<typ>;
using fbasetyp = fortran_type_t<basetyp>;
// 定义类型别名,ftyp代表typ的Fortran类型,fbasetyp代表basetyp的Fortran类型
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
fortran_int lwork;
fortran_int lrwork;
fortran_int liwork;
// 声明指针和整型变量
npy_uint8 *a, *w, *work, *rwork, *iwork;
// 声明指针变量用于存储内存分配后的位置
size_t safe_N = N;
fortran_int lda = fortran_int_max(N, 1);
// 初始化safe_N为N,计算lda作为N和1中较大的值
mem_buff = (npy_uint8 *)malloc(safe_N * safe_N * sizeof(typ) +
safe_N * sizeof(basetyp));
// 分配内存给mem_buff,大小为safe_N * safe_N * sizeof(typ) + safe_N * sizeof(basetyp)
if (!mem_buff) {
goto error;
}
// 如果内存分配失败,跳转到error标签
a = mem_buff;
w = mem_buff + safe_N * safe_N * sizeof(typ);
// 设置a和w指向内存分配后的位置
params->A = (typ*)a;
params->W = (basetyp*)w;
params->N = N;
params->JOBZ = JOBZ;
params->UPLO = UPLO;
params->LDA = lda;
// 设置params结构体的成员变量为分配后的内存地址和其他参数值
/* Work size query */
{
ftyp query_work_size;
fbasetyp query_rwork_size;
fortran_int query_iwork_size;
params->LWORK = -1;
params->LRWORK = -1;
params->LIWORK = -1;
params->WORK = (typ*)&query_work_size;
params->RWORK = (basetyp*)&query_rwork_size;
params->IWORK = &query_iwork_size;
if (call_evd(params) != 0) {
goto error;
}
lwork = (fortran_int)*(fbasetyp*)&query_work_size;
lrwork = (fortran_int)query_rwork_size;
liwork = query_iwork_size;
}
// 执行工作大小查询,并将结果存储在query_work_size、query_rwork_size和query_iwork_size中
mem_buff2 = (npy_uint8 *)malloc(lwork*sizeof(typ) +
lrwork*sizeof(basetyp) +
liwork*sizeof(fortran_int));
// 分配内存给mem_buff2,大小为lwork*sizeof(typ) + lrwork*sizeof(basetyp) + liwork*sizeof(fortran_int)
if (!mem_buff2) {
goto error;
}
// 如果内存分配失败,跳转到error标签
work = mem_buff2;
rwork = work + lwork*sizeof(typ);
iwork = rwork + lrwork*sizeof(basetyp);
// 设置work、rwork和iwork指向内存分配后的位置
params->WORK = (typ*)work;
params->RWORK = (basetyp*)rwork;
params->IWORK = (fortran_int*)iwork;
params->LWORK = lwork;
params->LRWORK = lrwork;
params->LIWORK = liwork;
// 更新params结构体的成员变量为新分配的内存地址和大小
return 1;
/* something failed */
error:
memset(params, 0, sizeof(*params));
free(mem_buff2);
free(mem_buff);
// 在出错时释放内存和清零params结构体
return 0;
}
/*
* (M, M)->(M,)(M, M)
* dimensions[1] -> M
* args[0] -> A[in]
* args[1] -> W
* args[2] -> A[out]
*/
template<typename typ>
static inline void
release_evd(EIGH_PARAMS_t<typ> *params)
{
/* allocated memory in A and WORK */
free(params->A);
free(params->WORK);
memset(params, 0, sizeof(*params));
}
// 释放params结构体中A和WORK成员变量所分配的内存,并清零params结构体本身
template<typename typ>
static inline void
eigh_wrapper(char JOBZ,
char UPLO,
char**args,
npy_intp const *dimensions,
npy_intp const *steps)
{
using basetyp = basetype_t<typ>;
ptrdiff_t outer_steps[3];
size_t iter;
size_t outer_dim = *dimensions++;
size_t op_count = (JOBZ=='N')?2:3;
EIGH_PARAMS_t<typ> eigh_params;
int error_occurred = get_fp_invalid_and_clear();
for (iter = 0; iter < op_count; ++iter) {
outer_steps[iter] = (ptrdiff_t) steps[iter];
}
steps += op_count;
// 初始化函数参数和局部变量
if (init_evd(&eigh_params,
JOBZ,
UPLO,
(fortran_int)dimensions[0], dispatch_scalar<typ>())) {
linearize_data matrix_in_ld = init_linearize_data(eigh_params.N, eigh_params.N, steps[1], steps[0]);
linearize_data eigenvalues_out_ld = init_linearize_data(1, eigh_params.N, 0, steps[2]);
linearize_data eigenvectors_out_ld = {}; /* silence uninitialized warning */
if ('V' == eigh_params.JOBZ) {
eigenvectors_out_ld = init_linearize_data(eigh_params.N, eigh_params.N, steps[4], steps[3]);
}
for (iter = 0; iter < outer_dim; ++iter) {
int not_ok;
linearize_matrix((typ*)eigh_params.A, (typ*)args[0], &matrix_in_ld);
not_ok = call_evd(&eigh_params);
if (!not_ok) {
delinearize_matrix((basetyp*)args[1],
(basetyp*)eigh_params.W,
&eigenvalues_out_ld);
if ('V' == eigh_params.JOBZ) {
delinearize_matrix((typ*)args[2],
(typ*)eigh_params.A,
&eigenvectors_out_ld);
}
} else {
error_occurred = 1;
nan_matrix((basetyp*)args[1], &eigenvalues_out_ld);
if ('V' == eigh_params.JOBZ) {
nan_matrix((typ*)args[2], &eigenvectors_out_ld);
}
}
update_pointers((npy_uint8**)args, outer_steps, op_count);
}
release_evd(&eigh_params);
}
set_fp_invalid_or_clear(error_occurred);
/* -------------------------------------------------------------------------- */
/* Solve family (includes inv) */
template<typename typ>
struct GESV_PARAMS_t
{
typ *A; /* A is (N, N) of base type */
typ *B; /* B is (N, NRHS) of base type */
fortran_int * IPIV; /* IPIV is (N) */
fortran_int N; /* Number of rows and columns in matrix A */
fortran_int NRHS; /* Number of right-hand side vectors in matrix B */
fortran_int LDA; /* Leading dimension of matrix A */
fortran_int LDB; /* Leading dimension of matrix B */
};
/* Calls LAPACK function sgesv for single precision real numbers */
static inline fortran_int
call_gesv(GESV_PARAMS_t<fortran_real> *params)
{
fortran_int rv;
LAPACK(sgesv)(¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->IPIV,
params->B, ¶ms->LDB,
&rv);
return rv;
}
/* Calls LAPACK function dgesv for double precision real numbers */
static inline fortran_int
call_gesv(GESV_PARAMS_t<fortran_doublereal> *params)
{
fortran_int rv;
LAPACK(dgesv)(¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->IPIV,
params->B, ¶ms->LDB,
&rv);
return rv;
}
/* Calls LAPACK function cgesv for single precision complex numbers */
static inline fortran_int
call_gesv(GESV_PARAMS_t<fortran_complex> *params)
{
fortran_int rv;
LAPACK(cgesv)(¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->IPIV,
params->B, ¶ms->LDB,
&rv);
return rv;
}
/* Calls LAPACK function zgesv for double precision complex numbers */
static inline fortran_int
call_gesv(GESV_PARAMS_t<fortran_doublecomplex> *params)
{
fortran_int rv;
LAPACK(zgesv)(¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->IPIV,
params->B, ¶ms->LDB,
&rv);
return rv;
}
/*
* Initialize the parameters to use in for the LAPACK function _heev
* Handles buffer allocation
*/
template<typename ftyp>
static inline int
init_gesv(GESV_PARAMS_t<ftyp> *params, fortran_int N, fortran_int NRHS)
{
npy_uint8 *mem_buff = NULL;
npy_uint8 *a, *b, *ipiv;
size_t safe_N = N;
size_t safe_NRHS = NRHS;
fortran_int ld = fortran_int_max(N, 1);
mem_buff = (npy_uint8 *)malloc(safe_N * safe_N * sizeof(ftyp) +
safe_N * safe_NRHS*sizeof(ftyp) +
safe_N * sizeof(fortran_int));
if (!mem_buff) {
goto error;
}
a = mem_buff;
b = a + safe_N * safe_N * sizeof(ftyp);
ipiv = b + safe_N * safe_NRHS * sizeof(ftyp);
params->A = (ftyp*)a;
params->B = (ftyp*)b;
params->IPIV = (fortran_int*)ipiv;
params->N = N;
params->NRHS = NRHS;
params->LDA = ld;
params->LDB = ld;
return 1;
error:
free(mem_buff);
memset(params, 0, sizeof(*params));
return 0;
}
template<typename ftyp>
static inline void
release_gesv(GESV_PARAMS_t<ftyp> *params)
{
/* 释放 params 结构体中的 A 成员所指向的内存块 */
free(params->A);
/* 将 params 结构体清零 */
memset(params, 0, sizeof(*params));
}
template<typename typ>
static void
solve(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
/* 定义 ftyp 为 typ 对应的 Fortran 类型 */
using ftyp = fortran_type_t<typ>;
/* 定义 GESV_PARAMS_t 结构体实例 params */
GESV_PARAMS_t<ftyp> params;
/* 定义 fortran_int 类型变量 n 和 nrhs */
fortran_int n, nrhs;
/* 获取并清除浮点无效标志 */
int error_occurred = get_fp_invalid_and_clear();
/* 初始化外层循环控制 */
INIT_OUTER_LOOP_3
/* 将 dimensions 数组中的第一个元素转换为 fortran_int 类型赋值给 n */
n = (fortran_int)dimensions[0];
/* 将 dimensions 数组中的第二个元素转换为 fortran_int 类型赋值给 nrhs */
nrhs = (fortran_int)dimensions[1];
/* 如果初始化 gesv 函数返回非零值,执行以下代码块 */
if (init_gesv(¶ms, n, nrhs)) {
/* 初始化 linearize_data 结构体 a_in、b_in、r_out */
linearize_data a_in = init_linearize_data(n, n, steps[1], steps[0]);
linearize_data b_in = init_linearize_data(nrhs, n, steps[3], steps[2]);
linearize_data r_out = init_linearize_data(nrhs, n, steps[5], steps[4]);
/* 开始外层循环 */
BEGIN_OUTER_LOOP_3
int not_ok;
/* 将 args[0] 的数据线性化到 params.A 中 */
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
/* 将 args[1] 的数据线性化到 params.B 中 */
linearize_matrix((typ*)params.B, (typ*)args[1], &b_in);
/* 调用 gesv 函数并将返回值赋给 not_ok */
not_ok = call_gesv(¶ms);
/* 如果 gesv 函数返回正常,将 params.B 的数据反线性化到 args[2] 中 */
if (!not_ok) {
delinearize_matrix((typ*)args[2], (typ*)params.B, &r_out);
} else {
/* 如果 gesv 函数返回异常,将 args[2] 的数据设置为 NaN */
error_occurred = 1;
nan_matrix((typ*)args[2], &r_out);
}
/* 结束外层循环 */
END_OUTER_LOOP
/* 释放 params 结构体所占用的资源 */
release_gesv(¶ms);
}
/* 设置浮点无效标志 */
set_fp_invalid_or_clear(error_occurred);
}
template<typename typ>
static void
solve1(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
/* 定义 ftyp 为 typ 对应的 Fortran 类型 */
using ftyp = fortran_type_t<typ>;
/* 定义 GESV_PARAMS_t 结构体实例 params */
GESV_PARAMS_t<ftyp> params;
/* 获取并清除浮点无效标志 */
int error_occurred = get_fp_invalid_and_clear();
/* 定义 fortran_int 类型变量 n */
fortran_int n;
/* 初始化外层循环控制 */
INIT_OUTER_LOOP_3
/* 将 dimensions 数组中的第一个元素转换为 fortran_int 类型赋值给 n */
n = (fortran_int)dimensions[0];
/* 如果初始化 gesv 函数返回非零值,执行以下代码块 */
if (init_gesv(¶ms, n, 1)) {
/* 初始化 linearize_data 结构体 a_in、b_in、r_out */
linearize_data a_in = init_linearize_data(n, n, steps[1], steps[0]);
linearize_data b_in = init_linearize_data(1, n, 1, steps[2]);
linearize_data r_out = init_linearize_data(1, n, 1, steps[3]);
/* 开始外层循环 */
BEGIN_OUTER_LOOP_3
int not_ok;
/* 将 args[0] 的数据线性化到 params.A 中 */
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
/* 将 args[1] 的数据线性化到 params.B 中 */
linearize_matrix((typ*)params.B, (typ*)args[1], &b_in);
/* 调用 gesv 函数并将返回值赋给 not_ok */
not_ok = call_gesv(¶ms);
/* 如果 gesv 函数返回正常,将 params.B 的数据反线性化到 args[2] 中 */
if (!not_ok) {
delinearize_matrix((typ*)args[2], (typ*)params.B, &r_out);
} else {
/* 如果 gesv 函数返回异常,将 args[2] 的数据设置为 NaN */
error_occurred = 1;
nan_matrix((typ*)args[2], &r_out);
}
/* 结束外层循环 */
END_OUTER_LOOP
/* 释放 params 结构体所占用的资源 */
release_gesv(¶ms);
}
/* 设置浮点无效标志 */
set_fp_invalid_or_clear(error_occurred);
}
template<typename typ>
static void
inv(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
/* 定义 ftyp 为 typ 对应的 Fortran 类型 */
using ftyp = fortran_type_t<typ>;
/* 定义 GESV_PARAMS_t 结构体实例 params */
GESV_PARAMS_t<ftyp> params;
/* 定义 fortran_int 类型变量 n */
fortran_int n;
/* 获取并清除浮点无效标志 */
int error_occurred = get_fp_invalid_and_clear();
/* 初始化外层循环控制 */
INIT_OUTER_LOOP_2
/* 将 dimensions 数组中的第一个元素转换为 fortran_int 类型赋值给 n */
n = (fortran_int)dimensions[0];
// 如果初始化参数失败,则不执行以下代码块
if (init_gesv(¶ms, n, n)) {
// 初始化用于线性化数据的输入和输出结构体
linearize_data a_in = init_linearize_data(n, n, steps[1], steps[0]);
linearize_data r_out = init_linearize_data(n, n, steps[3], steps[2]);
// 开始外层循环
BEGIN_OUTER_LOOP_2
int not_ok;
// 将参数 A 线性化为输入矩阵 a_in
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
// 初始化参数 B 为单位矩阵
identity_matrix((typ*)params.B, n);
// 调用线性方程求解函数 gesv
not_ok = call_gesv(¶ms);
// 如果求解成功,则将结果反线性化到输出矩阵 args[1]
if (!not_ok) {
delinearize_matrix((typ*)args[1], (typ*)params.B, &r_out);
} else {
// 如果求解失败,则标记错误,并将输出矩阵设置为 NaN
error_occurred = 1;
nan_matrix((typ*)args[1], &r_out);
}
// 结束外层循环
END_OUTER_LOOP
// 释放参数对象的资源
release_gesv(¶ms);
}
// 设置浮点数异常标志或清除错误状态
set_fp_invalid_or_clear(error_occurred);
/* -------------------------------------------------------------------------- */
/* Cholesky decomposition */
template<typename typ>
struct POTR_PARAMS_t
{
typ *A; // 指向矩阵的指针
fortran_int N; // 矩阵的维度
fortran_int LDA; // 矩阵的leading dimension
char UPLO; // 指示矩阵是上三角还是下三角
};
/* zero the undefined part in a upper/lower triangular matrix */
/* Note: matrix from fortran routine, so column-major order */
template<typename typ>
static inline void
zero_lower_triangle(POTR_PARAMS_t<typ> *params)
{
fortran_int n = params->N; // 获取矩阵的维度
typ *matrix = params->A; // 获取矩阵的起始地址
fortran_int i, j;
for (i = 0; i < n-1; ++i) { // 循环遍历矩阵的行
for (j = i+1; j < n; ++j) { // 循环遍历矩阵的列
matrix[j] = numeric_limits<typ>::zero; // 将矩阵的下三角部分清零
}
matrix += n; // 移动到下一行的起始位置
}
}
template<typename typ>
static inline void
zero_upper_triangle(POTR_PARAMS_t<typ> *params)
{
fortran_int n = params->N; // 获取矩阵的维度
typ *matrix = params->A; // 获取矩阵的起始地址
fortran_int i, j;
matrix += n; // 移动到矩阵的第二行起始位置
for (i = 1; i < n; ++i) { // 循环遍历矩阵的行
for (j = 0; j < i; ++j) { // 循环遍历矩阵的列
matrix[j] = numeric_limits<typ>::zero; // 将矩阵的上三角部分清零
}
matrix += n; // 移动到下一行的起始位置
}
}
static inline fortran_int
call_potrf(POTR_PARAMS_t<fortran_real> *params)
{
fortran_int rv;
LAPACK(spotrf)(¶ms->UPLO,
¶ms->N, params->A, ¶ms->LDA,
&rv);
return rv;
}
static inline fortran_int
call_potrf(POTR_PARAMS_t<fortran_doublereal> *params)
{
fortran_int rv;
LAPACK(dpotrf)(¶ms->UPLO,
¶ms->N, params->A, ¶ms->LDA,
&rv);
return rv;
}
static inline fortran_int
call_potrf(POTR_PARAMS_t<fortran_complex> *params)
{
fortran_int rv;
LAPACK(cpotrf)(¶ms->UPLO,
¶ms->N, params->A, ¶ms->LDA,
&rv);
return rv;
}
static inline fortran_int
call_potrf(POTR_PARAMS_t<fortran_doublecomplex> *params)
{
fortran_int rv;
LAPACK(zpotrf)(¶ms->UPLO,
¶ms->N, params->A, ¶ms->LDA,
&rv);
return rv;
}
template<typename ftyp>
static inline int
init_potrf(POTR_PARAMS_t<ftyp> *params, char UPLO, fortran_int N)
{
npy_uint8 *mem_buff = NULL; // 内存缓冲区指针
npy_uint8 *a; // 矩阵起始地址指针
size_t safe_N = N;
fortran_int lda = fortran_int_max(N, 1); // 计算leading dimension
mem_buff = (npy_uint8 *)malloc(safe_N * safe_N * sizeof(ftyp)); // 分配内存缓冲区
if (!mem_buff) {
goto error;
}
a = mem_buff;
params->A = (ftyp*)a; // 设置矩阵指针
params->N = N; // 设置矩阵维度
params->LDA = lda; // 设置leading dimension
params->UPLO = UPLO; // 设置矩阵的上/下三角属性
return 1;
error:
free(mem_buff); // 释放内存缓冲区
memset(params, 0, sizeof(*params)); // 将参数结构体清零
return 0;
}
template<typename ftyp>
static inline void
release_potrf(POTR_PARAMS_t<ftyp> *params)
{
/* memory block base in A */
free(params->A); // 释放矩阵内存
memset(params, 0, sizeof(*params)); // 将参数结构体清零
}
template<typename typ>
static void
cholesky(char uplo, char **args, npy_intp const *dimensions, npy_intp const *steps)
{
using ftyp = fortran_type_t<typ>; // 定义Fortran类型
POTR_PARAMS_t<ftyp> params;
int error_occurred = get_fp_invalid_and_clear();
fortran_int n;
INIT_OUTER_LOOP_2
n = (fortran_int)dimensions[0];
if (init_potrf(¶ms, uplo, n)) {
linearize_data a_in = init_linearize_data(n, n, steps[1], steps[0]);
linearize_data r_out = init_linearize_data(n, n, steps[3], steps[2]);
BEGIN_OUTER_LOOP_2
int not_ok;
linearize_matrix(params.A, (ftyp*)args[0], &a_in);
not_ok = call_potrf(¶ms);
if (!not_ok) {
if (uplo == 'L') {
zero_upper_triangle(¶ms);
}
else {
zero_lower_triangle(¶ms);
}
delinearize_matrix((ftyp*)args[1], params.A, &r_out);
} else {
error_occurred = 1;
nan_matrix((ftyp*)args[1], &r_out);
}
END_OUTER_LOOP
release_potrf(¶ms);
}
set_fp_invalid_or_clear(error_occurred);
}
// 模板函数:调用 cholesky 函数处理下三角矩阵
template<typename typ>
static void
cholesky_lo(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
cholesky<typ>('L', args, dimensions, steps);
}
// 模板函数:调用 cholesky 函数处理上三角矩阵
template<typename typ>
static void
cholesky_up(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
cholesky<typ>('U', args, dimensions, steps);
}
/* -------------------------------------------------------------------------- */
/* eig family */
// 结构体模板:包含用于 LAPACK 中 GEEV 函数的参数
template<typename typ>
struct GEEV_PARAMS_t {
typ *A; // 矩阵 A
basetype_t<typ> *WR; /* RWORK in complex versions, REAL W buffer for (sd)geev*/
typ *WI; // 虚部分数组
typ *VLR; // 左本征向量部分
typ *VRR; // 右本征向量部分
typ *WORK; // 工作区数组
typ *W; // 最终本征值数组
typ *VL; // 最终左本征向量数组
typ *VR; // 最终右本征向量数组
fortran_int N; // 矩阵的阶数
fortran_int LDA; // A 的第一维长度
fortran_int LDVL; // VL 的第一维长度
fortran_int LDVR; // VR 的第一维长度
fortran_int LWORK; // 工作区长度
char JOBVL; // 计算左本征向量的选项
char JOBVR; // 计算右本征向量的选项
};
// 函数模板:打印 GEEV 参数结构体的内容
template<typename typ>
static inline void
dump_geev_params(const char *name, GEEV_PARAMS_t<typ>* params)
{
TRACE_TXT("\n%s\n"
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %p\n"\
"\t%10s: %d\n"\
"\t%10s: %d\n"\
"\t%10s: %d\n"\
"\t%10s: %d\n"\
"\t%10s: %d\n"\
"\t%10s: %c\n"\
"\t%10s: %c\n",
name,
"A", params->A,
"WR", params->WR,
"WI", params->WI,
"VLR", params->VLR,
"VRR", params->VRR,
"WORK", params->WORK,
"W", params->W,
"VL", params->VL,
"VR", params->VR,
"N", (int)params->N,
"LDA", (int)params->LDA,
"LDVL", (int)params->LDVL,
"LDVR", (int)params->LDVR,
"LWORK", (int)params->LWORK,
"JOBVL", params->JOBVL,
"JOBVR", params->JOBVR);
}
// 函数:调用 LAPACK 中的 sgeev 函数处理 float 类型的 GEEV 参数
static inline fortran_int
call_geev(GEEV_PARAMS_t<float>* params)
{
fortran_int rv;
LAPACK(sgeev)(¶ms->JOBVL, ¶ms->JOBVR,
¶ms->N, params->A, ¶ms->LDA,
params->WR, params->WI,
params->VLR, ¶ms->LDVL,
params->VRR, ¶ms->LDVR,
params->WORK, ¶ms->LWORK,
&rv);
return rv;
}
// 函数:调用 LAPACK 中的 sgeev 函数处理 double 类型的 GEEV 参数
static inline fortran_int
call_geev(GEEV_PARAMS_t<double>* params)
{
fortran_int rv;
LAPACK(dgeev)(¶ms->JOBVL, ¶ms->JOBVR,
¶ms->N, params->A, ¶ms->LDA,
params->WR, params->WI,
params->VLR, ¶ms->LDVL,
params->VRR, ¶ms->LDVR,
params->WORK, ¶ms->LWORK,
&rv);
return rv;
}
LAPACK(dgeev)(¶ms->JOBVL, ¶ms->JOBVR,
¶ms->N, params->A, ¶ms->LDA,
params->WR, params->WI,
params->VLR, ¶ms->LDVL,
params->VRR, ¶ms->LDVR,
params->WORK, ¶ms->LWORK,
&rv);
return rv;
template<typename typ>
static inline int
init_geev(GEEV_PARAMS_t<typ> *params, char jobvl, char jobvr, fortran_int n,
scalar_trait)
{
// 指向内存缓冲区的指针,初始化为NULL
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
// 指向各种数据的指针,初始为NULL
npy_uint8 *a, *wr, *wi, *vlr, *vrr, *work, *w, *vl, *vr;
// 将 n 转为 size_t 类型,确保安全使用
size_t safe_n = n;
// 计算各个数组所需内存大小
size_t a_size = safe_n * safe_n * sizeof(typ);
size_t wr_size = safe_n * sizeof(typ);
size_t wi_size = safe_n * sizeof(typ);
size_t vlr_size = jobvl=='V' ? safe_n * safe_n * sizeof(typ) : 0;
size_t vrr_size = jobvr=='V' ? safe_n * safe_n * sizeof(typ) : 0;
size_t w_size = wr_size * 2;
size_t vl_size = vlr_size * 2;
size_t vr_size = vrr_size * 2;
size_t work_count = 0;
// 计算 ld 的值
fortran_int ld = fortran_int_max(n, 1);
/* allocate data for known sizes (all but work) */
// 分配内存缓冲区,包括所有数组的大小,除了 work
mem_buff = (npy_uint8 *)malloc(a_size + wr_size + wi_size +
vlr_size + vrr_size +
w_size + vl_size + vr_size);
if (!mem_buff) {
goto error; // 分配失败,跳转到错误处理
}
// 分配各个数组的内存位置
a = mem_buff;
wr = a + a_size;
wi = wr + wr_size;
vlr = wi + wi_size;
vrr = vlr + vlr_size;
w = vrr + vrr_size;
vl = w + w_size;
vr = vl + vl_size;
// 将参数结构体中的指针指向对应的数组
params->A = (typ*)a;
params->WR = (typ*)wr;
params->WI = (typ*)wi;
params->VLR = (typ*)vlr;
params->VRR = (typ*)vrr;
params->W = (typ*)w;
params->VL = (typ*)vl;
params->VR = (typ*)vr;
params->N = n;
params->LDA = ld;
params->LDVL = ld;
params->LDVR = ld;
params->JOBVL = jobvl;
params->JOBVR = jobvr;
/* Work size query */
// 查询所需的 work 大小
{
typ work_size_query;
params->LWORK = -1; // 设置 LWORK 为 -1
params->WORK = &work_size_query; // 将 WORK 指向 work_size_query
// 调用 call_geev 函数查询工作空间大小
if (call_geev(params) != 0) {
goto error; // 调用失败,跳转到错误处理
}
work_count = (size_t)work_size_query; // 获取查询得到的工作空间大小
}
// 分配工作空间的内存
mem_buff2 = (npy_uint8 *)malloc(work_count * sizeof(typ));
if (!mem_buff2) {
goto error; // 分配失败,跳转到错误处理
}
work = mem_buff2; // 设置 work 指向分配的内存
// 设置参数结构体中的 LWORK 和 WORK
params->LWORK = (fortran_int)work_count;
params->WORK = (typ*)work;
return 1; // 成功初始化,返回1
error:
free(mem_buff2); // 释放分配的内存
free(mem_buff);
memset(params, 0, sizeof(*params)); // 将 params 结构体清零
return 0; // 返回初始化失败
}
for (iter = 0; iter < n; ++iter) {
typ re = r[iter];
typ im = r[iter+n];
c[iter].r = re;
c[iter].i = im;
c[iter+n].r = re;
c[iter+n].i = -im;
}
/*
* make the complex eigenvectors from the real array produced by sgeev/zgeev.
* c is the array where the results will be left.
* r is the source array of reals produced by sgeev/zgeev
* i is the eigenvalue imaginary part produced by sgeev/zgeev
* n is so that the order of the matrix is n by n
*/
template<typename complextyp, typename typ>
static inline void
mk_geev_complex_eigenvectors(complextyp *c,
const typ *r,
const typ *i,
size_t n)
{
// 初始化迭代器
size_t iter = 0;
// 迭代处理每个特征值
while (iter < n)
{
// 如果特征值的虚部为零,则为实特征值,生成实特征向量数组
if (i[iter] == numeric_limits<typ>::zero) {
/* eigenvalue was real, eigenvectors as well... */
mk_complex_array_from_real(c, r, n);
c += n; // 移动复数结果数组指针到下一个位置
r += n; // 移动实数源数组指针到下一个位置
iter ++; // 增加迭代器
} else {
// 特征值为复数,则生成一对复数特征向量
mk_complex_array_conjugate_pair(c, r, n);
c += 2*n; // 移动复数结果数组指针到下一个位置的两倍
r += 2*n; // 移动实数源数组指针到下一个位置的两倍
iter += 2; // 增加迭代器两倍
}
}
}
template<typename complextyp, typename typ>
static inline void
process_geev_results(GEEV_PARAMS_t<typ> *params, scalar_trait)
{
/* REAL versions of geev need the results to be translated
* into complex versions. This is the way to deal with imaginary
* results. In our gufuncs we will always return complex arrays!
*/
// 将实数特征值结果转换为复数数组
mk_complex_array((complextyp*)params->W, (typ*)params->WR, (typ*)params->WI, params->N);
/* handle the eigenvectors */
// 处理特征向量
if ('V' == params->JOBVL) {
// 如果需要左特征向量,则生成复数左特征向量数组
mk_geev_complex_eigenvectors((complextyp*)params->VL, (typ*)params->VLR,
(typ*)params->WI, params->N);
}
if ('V' == params->JOBVR) {
// 如果需要右特征向量,则生成复数右特征向量数组
mk_geev_complex_eigenvectors((complextyp*)params->VR, (typ*)params->VRR,
(typ*)params->WI, params->N);
}
}
static inline fortran_int
call_geev(GEEV_PARAMS_t<fortran_complex>* params)
{
fortran_int rv;
LAPACK(cgeev)(¶ms->JOBVL, ¶ms->JOBVR,
¶ms->N, params->A, ¶ms->LDA,
params->W,
params->VL, ¶ms->LDVL,
params->VR, ¶ms->LDVR,
params->WORK, ¶ms->LWORK,
params->WR, /* actually RWORK */
&rv);
return rv;
}
static inline fortran_int
call_geev(GEEV_PARAMS_t<fortran_doublecomplex>* params)
{
fortran_int rv;
LAPACK(zgeev)(¶ms->JOBVL, ¶ms->JOBVR,
¶ms->N, params->A, ¶ms->LDA,
params->W,
params->VL, ¶ms->LDVL,
params->VR, ¶ms->LDVR,
params->WORK, ¶ms->LWORK,
params->WR, /* actually RWORK */
&rv);
return rv;
}
// 初始化 GEEV 算法的参数结构体
template<typename ftyp>
static inline int
init_geev(GEEV_PARAMS_t<ftyp>* params,
char jobvl,
char jobvr,
fortran_int n, complex_trait)
{
// 定义实数类型为 ftyp 对应的基础类型
using realtyp = basetype_t<ftyp>;
// 初始化指针变量为 NULL
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
npy_uint8 *a, *w, *vl, *vr, *work, *rwork;
// 将 n 赋值给安全大小
size_t safe_n = n;
// 计算并分配内存空间大小
size_t a_size = safe_n * safe_n * sizeof(ftyp);
size_t w_size = safe_n * sizeof(ftyp);
size_t vl_size = jobvl=='V'? safe_n * safe_n * sizeof(ftyp) : 0;
size_t vr_size = jobvr=='V'? safe_n * safe_n * sizeof(ftyp) : 0;
size_t rwork_size = 2 * safe_n * sizeof(realtyp);
size_t work_count = 0;
size_t total_size = a_size + w_size + vl_size + vr_size + rwork_size;
// 计算矩阵 A 的列数
fortran_int ld = fortran_int_max(n, 1);
// 分配总内存空间
mem_buff = (npy_uint8 *)malloc(total_size);
if (!mem_buff) {
goto error;
}
// 将内存空间分配给各个数组指针
a = mem_buff;
w = a + a_size;
vl = w + w_size;
vr = vl + vl_size;
rwork = vr + vr_size;
// 设置参数结构体中的成员指针
params->A = (ftyp*)a;
params->WR = (realtyp*)rwork;
params->WI = NULL;
params->VLR = NULL;
params->VRR = NULL;
params->VL = (ftyp*)vl;
params->VR = (ftyp*)vr;
params->W = (ftyp*)w;
params->N = n;
params->LDA = ld;
params->LDVL = ld;
params->LDVR = ld;
params->JOBVL = jobvl;
params->JOBVR = jobvr;
/* Work size query */
{
// 定义用于查询工作空间大小的变量
ftyp work_size_query;
// 设置 LWORK 为 -1,以查询所需的工作空间大小
params->LWORK = -1;
params->WORK = &work_size_query;
// 调用 GEEV 函数查询工作空间大小,若失败则跳转到错误处理
if (call_geev(params) != 0) {
goto error;
}
// 获取实际所需的工作空间大小并修复 Lapack 3.0.0 中的一个 bug
work_count = (size_t) work_size_query.r;
if(work_count == 0) work_count = 1;
}
// 分配所需的工作空间
mem_buff2 = (npy_uint8 *)malloc(work_count*sizeof(ftyp));
if (!mem_buff2) {
goto error;
}
// 将工作空间内存指针赋给 params 结构体中的 WORK 成员
work = mem_buff2;
params->LWORK = (fortran_int)work_count;
params->WORK = (ftyp*)work;
// 初始化成功,返回 1
return 1;
error:
// 发生错误时释放内存空间并清空 params 结构体
free(mem_buff2);
free(mem_buff);
memset(params, 0, sizeof(*params));
// 返回 0 表示初始化失败
return 0;
}
// 处理 GEEV 算法的结果,复数版本无需处理
template<typename complextyp, typename typ>
static inline void
process_geev_results(GEEV_PARAMS_t<typ> *NPY_UNUSED(params), complex_trait)
{
/* nothing to do here, complex versions are ready to copy out */
}
// 释放 GEEV 算法所使用的内存空间
template<typename typ>
static inline void
release_geev(GEEV_PARAMS_t<typ> *params)
{
// 释放工作空间和矩阵 A 的内存空间,并清空 params 结构体
free(params->WORK);
free(params->A);
memset(params, 0, sizeof(*params));
}
// 对外提供的包装函数,用于调用 GEEV 算法
template<typename fctype, typename ftype>
static inline void
eig_wrapper(char JOBVL,
char JOBVR,
char**args,
npy_intp const *dimensions,
npy_intp const *steps)
{
// 外部维度数组和迭代器变量
ptrdiff_t outer_steps[4];
size_t iter;
size_t outer_dim = *dimensions++;
size_t op_count = 2;
int error_occurred = get_fp_invalid_and_clear();
GEEV_PARAMS_t<ftype> geev_params;
// 断言 JOBVL 必须为 'N',即不计算左特征向量
assert(JOBVL == 'N');
// 堆栈追踪
STACK_TRACE;
// 根据 JOBVL 和 JOBVR 的值更新操作计数
op_count += 'V'==JOBVL?1:0;
op_count += 'V'==JOBVR?1:0;
}
for (iter = 0; iter < op_count; ++iter) {
outer_steps[iter] = (ptrdiff_t) steps[iter];
}
steps += op_count;
// 将 steps 数组中的前 op_count 个元素转换为 ptrdiff_t 类型并存储到 outer_steps 数组中,然后将 steps 指针向后移动 op_count 个位置。
if (init_geev(&geev_params,
JOBVL, JOBVR,
(fortran_int)dimensions[0], dispatch_scalar<ftype>())) {
// 调用 init_geev 函数初始化 geev_params 结构体,传递 JOBVL、JOBVR、dimensions[0] 和 dispatch_scalar<ftype>() 的值作为参数。如果 init_geev 返回非零值(表示初始化成功),则执行以下代码块。
linearize_data vl_out = {}; /* silence uninitialized warning */
linearize_data vr_out = {}; /* silence uninitialized warning */
// 初始化 vl_out 和 vr_out 结构体,用于存储线性化的数据。这里使用空初始化列表来消除未初始化的警告。
linearize_data a_in = init_linearize_data(
geev_params.N, geev_params.N,
steps[1], steps[0]);
steps += 2;
// 调用 init_linearize_data 函数初始化 a_in 结构体,设置其维度和步长参数,并将 steps 指针向后移动两个位置。
linearize_data w_out = init_linearize_data(
1, geev_params.N,
0, steps[0]);
steps += 1;
// 调用 init_linearize_data 函数初始化 w_out 结构体,设置其维度和步长参数,并将 steps 指针向后移动一位。
if ('V' == geev_params.JOBVL) {
vl_out = init_linearize_data(
geev_params.N, geev_params.N,
steps[1], steps[0]);
steps += 2;
}
// 如果 geev_params.JOBVL 的值为 'V',则调用 init_linearize_data 函数初始化 vl_out 结构体,设置其维度和步长参数,并将 steps 指针向后移动两个位置。
if ('V' == geev_params.JOBVR) {
vr_out = init_linearize_data(
geev_params.N, geev_params.N,
steps[1], steps[0]);
}
// 如果 geev_params.JOBVR 的值为 'V',则调用 init_linearize_data 函数初始化 vr_out 结构体,设置其维度和步长参数。
for (iter = 0; iter < outer_dim; ++iter) {
int not_ok;
char **arg_iter = args;
/* copy the matrix in */
linearize_matrix((ftype*)geev_params.A, (ftype*)*arg_iter++, &a_in);
not_ok = call_geev(&geev_params);
if (!not_ok) {
process_geev_results<fctype>(&geev_params,
// 循环遍历 outer_dim 次执行以下操作:将 args 中的矩阵复制到 geev_params.A 中,调用 call_geev 函数执行 geev 计算,并检查计算是否成功。
/* -------------------------------------------------------------------------- */
/* singular value decomposition */
template<typename ftyp>
struct GESDD_PARAMS_t
{
ftyp *A; // 输入矩阵 A
basetype_t<ftyp> *S; // 奇异值数组 S
ftyp *U; // 左奇异向量 U
ftyp *VT; // 右奇异向量的转置 VT
ftyp *WORK; // 工作数组 WORK
basetype_t<ftyp> *RWORK; // 实数类型工作数组 RWORK
fortran_int *IWORK; // 整数类型工作数组 IWORK
fortran_int M; // A 的行数
fortran_int N; // A 的列数
fortran_int LDA; // A 的 leading dimension
fortran_int LDU; // U 的 leading dimension
fortran_int LDVT; // VT 的 leading dimension
fortran_int LWORK; // WORK 数组的长度
char JOBZ; // 指定是否计算 U 和 VT ('A', 'S', 'O', 'N')
} ;
template<typename ftyp>
static inline void
dump_gesdd_params(const char *name,
GESDD_PARAMS_t<ftyp> *params)
{
TRACE_TXT("\n%s:\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %15c'%c'\n",
name,
"A", params->A,
"S", params->S,
"U", params->U,
"VT", params->VT,
"WORK", params->WORK,
"RWORK", params->RWORK,
"IWORK", params->IWORK,
"M", (int)params->M,
"N", (int)params->N,
"LDA", (int)params->LDA,
"LDU", (int)params->LDU,
"LDVT", (int)params->LDVT,
"LWORK", (int)params->LWORK,
"JOBZ", ' ', params->JOBZ);
}
static inline int
compute_urows_vtcolumns(char jobz,
fortran_int m, fortran_int n,
fortran_int *urows, fortran_int *vtcolumns)
{
// 计算 m 和 n 的最小值
fortran_int min_m_n = fortran_int_min(m, n);
// 根据 jobz 的不同取值进行不同的操作
switch(jobz)
{
case 'N':
// 不计算 U 和 VT
*urows = 0;
*vtcolumns = 0;
break;
case 'A':
// 计算全部的 U 和 VT
*urows = m;
*vtcolumns = n;
break;
case 'S':
{
// 计算较小的 min_m_n 个 U 和 VT
*urows = min_m_n;
*vtcolumns = min_m_n;
}
break;
default:
// 默认情况下返回错误
return 0;
}
// 成功计算则返回 1
return 1;
}
static inline fortran_int
call_gesdd(GESDD_PARAMS_t<fortran_real> *params)
{
fortran_int rv;
// 调用 LAPACK 库中的单精度实数版本 gesdd 函数
LAPACK(sgesdd)(¶ms->JOBZ, ¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->S,
params->U, ¶ms->LDU,
params->VT, ¶ms->LDVT,
params->WORK, ¶ms->LWORK,
(fortran_int*)params->IWORK,
&rv);
// 返回 LAPACK 函数的返回值
return rv;
}
static inline fortran_int
call_gesdd(GESDD_PARAMS_t<fortran_doublereal> *params)
{
fortran_int rv;
// 调用 LAPACK 库中的双精度实数版本 gesdd 函数
LAPACK(dgesdd)(¶ms->JOBZ, ¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->S,
params->U, ¶ms->LDU,
params->VT, ¶ms->LDVT,
params->WORK, ¶ms->LWORK,
(fortran_int*)params->IWORK,
&rv);
// 返回 LAPACK 函数的返回值
return rv;
}
template<typename ftyp>
static inline int
init_gesdd(GESDD_PARAMS_t<ftyp> *params,
char jobz,
fortran_int m,
fortran_int n, scalar_trait)
{
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
npy_uint8 *a, *s, *u, *vt, *work, *iwork;
size_t safe_m = m;
size_t safe_n = n;
// 计算数组 a 的大小
size_t a_size = safe_m * safe_n * sizeof(ftyp);
// 计算 min_m_n
fortran_int min_m_n = fortran_int_min(m, n);
size_t safe_min_m_n = min_m_n;
// 计算数组 s 的大小
size_t s_size = safe_min_m_n * sizeof(ftyp);
fortran_int u_row_count, vt_column_count;
size_t safe_u_row_count, safe_vt_column_count;
size_t u_size, vt_size;
fortran_int work_count;
size_t work_size;
// 计算 iwork 的大小
size_t iwork_size = 8 * safe_min_m_n * sizeof(fortran_int);
// 计算 ld
fortran_int ld = fortran_int_max(m, 1);
// 根据 jobz 的值计算 u_row_count 和 vt_column_count
if (!compute_urows_vtcolumns(jobz, m, n, &u_row_count, &vt_column_count)) {
// 如果计算失败则跳转到 error 标签
goto error;
}
safe_u_row_count = u_row_count;
safe_vt_column_count = vt_column_count;
// 计算 u 和 vt 的大小
u_size = safe_u_row_count * safe_m * sizeof(ftyp);
vt_size = safe_n * safe_vt_column_count * sizeof(ftyp);
// 分配内存空间
mem_buff = (npy_uint8 *)malloc(a_size + s_size + u_size + vt_size + iwork_size);
if (!mem_buff) {
// 内存分配失败,跳转到 error 标签
goto error;
}
// 设置各个指针指向相应的内存位置
a = mem_buff;
s = a + a_size;
u = s + s_size;
vt = u + u_size;
iwork = vt + vt_size;
/* 使 vt_column_count 成为有效的 LAPACK 参数(0 不是有效值) */
vt_column_count = fortran_int_max(1, vt_column_count);
// 设置参数结构体中的维度信息
params->M = m; // 矩阵 A 的行数
params->N = n; // 矩阵 A 的列数
params->A = (ftyp*)a; // 矩阵 A 的数据
params->S = (ftyp*)s; // 奇异值数组
params->U = (ftyp*)u; // 左奇异向量矩阵
params->VT = (ftyp*)vt; // 右奇异向量转置矩阵
params->RWORK = NULL; // 用于实数工作空间的指针(未使用)
params->IWORK = (fortran_int*)iwork; // 用于整数工作空间的指针
params->LDA = ld; // 矩阵 A 的列数
params->LDU = ld; // 矩阵 U 的列数
params->LDVT = vt_column_count; // 矩阵 VT 的列数
params->JOBZ = jobz; // 指定计算选项
/* 查询工作空间大小 */
{
ftyp work_size_query; // 用于存储工作空间大小的变量
params->LWORK = -1; // 设置为-1表示查询工作空间大小
params->WORK = &work_size_query; // 指向存储工作空间大小的变量的指针
if (call_gesdd(params) != 0) { // 调用 LAPACK 函数查询工作空间大小
goto error; // 如果调用失败,跳转到错误处理
}
work_count = (fortran_int)work_size_query; // 获取查询得到的工作空间大小
/* 修复 LAPACK 3.0.0 中的一个 bug */
if (work_count == 0) work_count = 1; // 如果工作空间大小为 0,则设置为 1
work_size = (size_t)work_count * sizeof(ftyp); // 计算需要分配的工作空间大小
}
mem_buff2 = (npy_uint8 *)malloc(work_size); // 分配实际的工作空间内存
if (!mem_buff2) { // 内存分配失败处理
goto error; // 跳转到错误处理
}
work = mem_buff2; // 将分配的内存地址存入 work 指针
params->LWORK = work_count; // 设置参数结构体中的工作空间大小
params->WORK = (ftyp*)work; // 设置参数结构体中的工作空间指针
return 1; // 成功初始化,返回 1
error:
TRACE_TXT("%s failed init\n", __FUNCTION__); // 输出错误信息
free(mem_buff); // 释放之前分配的内存
free(mem_buff2); // 释放当前分配的工作空间内存
memset(params, 0, sizeof(*params)); // 将参数结构体清零
return 0; // 返回初始化失败
}
static inline fortran_int
call_gesdd(GESDD_PARAMS_t<fortran_complex> *params)
{
fortran_int rv;
// 调用 LAPACK 库函数 cgesdd 进行复数类型的奇异值分解
LAPACK(cgesdd)(¶ms->JOBZ, ¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->S,
params->U, ¶ms->LDU,
params->VT, ¶ms->LDVT,
params->WORK, ¶ms->LWORK,
params->RWORK,
params->IWORK,
&rv);
// 返回 LAPACK 函数调用的返回值
return rv;
}
static inline fortran_int
call_gesdd(GESDD_PARAMS_t<fortran_doublecomplex> *params)
{
fortran_int rv;
// 调用 LAPACK 库函数 zgesdd 进行双精度复数类型的奇异值分解
LAPACK(zgesdd)(¶ms->JOBZ, ¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->S,
params->U, ¶ms->LDU,
params->VT, ¶ms->LDVT,
params->WORK, ¶ms->LWORK,
params->RWORK,
params->IWORK,
&rv);
// 返回 LAPACK 函数调用的返回值
return rv;
}
template<typename ftyp>
static inline int
init_gesdd(GESDD_PARAMS_t<ftyp> *params,
char jobz,
fortran_int m,
fortran_int n, complex_trait)
{
using frealtyp = basetype_t<ftyp>;
npy_uint8 *mem_buff = NULL, *mem_buff2 = NULL;
npy_uint8 *a,*s, *u, *vt, *work, *rwork, *iwork;
size_t a_size, s_size, u_size, vt_size, work_size, rwork_size, iwork_size;
size_t safe_u_row_count, safe_vt_column_count;
fortran_int u_row_count, vt_column_count, work_count;
size_t safe_m = m;
size_t safe_n = n;
fortran_int min_m_n = fortran_int_min(m, n);
size_t safe_min_m_n = min_m_n;
fortran_int ld = fortran_int_max(m, 1);
// 根据参数计算安全的 U 和 VT 的行数和列数
if (!compute_urows_vtcolumns(jobz, m, n, &u_row_count, &vt_column_count)) {
goto error;
}
safe_u_row_count = u_row_count;
safe_vt_column_count = vt_column_count;
// 计算各个缓冲区的大小
a_size = safe_m * safe_n * sizeof(ftyp);
s_size = safe_min_m_n * sizeof(frealtyp);
u_size = safe_u_row_count * safe_m * sizeof(ftyp);
vt_size = safe_n * safe_vt_column_count * sizeof(ftyp);
rwork_size = 'N'==jobz?
(7 * safe_min_m_n) :
(5*safe_min_m_n * safe_min_m_n + 5*safe_min_m_n);
rwork_size *= sizeof(ftyp);
iwork_size = 8 * safe_min_m_n* sizeof(fortran_int);
// 分配内存缓冲区
mem_buff = (npy_uint8 *)malloc(a_size +
s_size +
u_size +
vt_size +
rwork_size +
iwork_size);
if (!mem_buff) {
goto error;
}
// 指定各个数据结构在内存缓冲区中的位置
a = mem_buff;
s = a + a_size;
u = s + s_size;
vt = u + u_size;
rwork = vt + vt_size;
iwork = rwork + rwork_size;
/* fix vt_column_count so that it is a valid lapack parameter (0 is not) */
// 调整 vt_column_count 以确保其为 LAPACK 函数的有效参数(0 是无效的)
vt_column_count = fortran_int_max(1, vt_column_count);
// 设置参数结构体中各个指针的值
params->A = (ftyp*)a;
params->S = (frealtyp*)s;
params->U = (ftyp*)u;
// 将参数指针指向传入的数组 vt
params->VT = (ftyp*)vt;
// 将参数指针指向传入的数组 rwork
params->RWORK = (frealtyp*)rwork;
// 将参数指针指向传入的数组 iwork
params->IWORK = (fortran_int*)iwork;
// 设置参数中的矩阵行数 M
params->M = m;
// 设置参数中的矩阵列数 N
params->N = n;
// 设置参数中的矩阵的 leading dimension LDA
params->LDA = ld;
// 设置参数中的左奇异矩阵的 leading dimension LDU
params->LDU = ld;
// 设置参数中的右奇异矩阵的 leading dimension LDVT
params->LDVT = vt_column_count;
// 设置参数中的计算选项 JOBZ
/* Work size query */
{
// 定义用于工作空间查询的变量
ftyp work_size_query;
// 设置 LWORK 为 -1 以查询所需的工作空间大小
params->LWORK = -1;
// 将 WORK 指向工作空间查询变量的地址
params->WORK = &work_size_query;
// 调用 gesdd 函数进行工作空间大小查询,并检查返回值
if (call_gesdd(params) != 0) {
// 如果调用失败,跳转到错误处理
goto error;
}
// 从工作空间查询结果中获取工作空间的大小
work_count = (fortran_int)(*(frealtyp*)&work_size_query);
// 修复 lapack 3.0.0 中的一个 bug
if (work_count == 0) work_count = 1;
// 计算实际需要的工作空间大小
work_size = (size_t)work_count * sizeof(ftyp);
}
// 分配工作空间的内存
mem_buff2 = (npy_uint8 *)malloc(work_size);
if (!mem_buff2) {
// 如果内存分配失败,跳转到错误处理
goto error;
}
// 将工作指针指向分配的内存空间
work = mem_buff2;
// 将参数中的 LWORK 设置为实际计算得到的工作空间大小
params->LWORK = work_count;
// 将参数中的 WORK 指针指向工作空间
params->WORK = (ftyp*)work;
// 成功初始化,返回 1
return 1;
error:
// 打印错误信息
TRACE_TXT("%s failed init\n", __FUNCTION__);
// 释放分配的内存空间
free(mem_buff2);
free(mem_buff);
// 将参数结构体清零
memset(params, 0, sizeof(*params));
// 返回初始化失败
return 0;
}
template<typename typ>
static inline void
release_gesdd(GESDD_PARAMS_t<typ>* params)
{
/* 释放 params 结构体中的 A 和 WORK 字段所分配的内存块 */
free(params->A);
free(params->WORK);
// 将 params 结构体的内存清零
memset(params, 0, sizeof(*params));
}
template<typename typ>
static inline void
svd_wrapper(char JOBZ,
char **args,
npy_intp const *dimensions,
npy_intp const *steps)
{
// 使用 basetype_t<typ> 定义 basetyp 类型
using basetyp = basetype_t<typ>;
// 声明一个指针数组 outer_steps,包含四个元素
ptrdiff_t outer_steps[4];
// 获取浮点无效操作的状态并清除
int error_occurred = get_fp_invalid_and_clear();
// 定义迭代器 iter
size_t iter;
// 获取外部维度的大小
size_t outer_dim = *dimensions++;
// 确定操作计数 op_count,如果 JOBZ 为 'N' 则为 2,否则为 4
size_t op_count = (JOBZ=='N')?2:4;
// 定义 GESDD_PARAMS_t<typ> 结构体变量 params
GESDD_PARAMS_t<typ> params;
// 将 steps 数组中的值赋给 outer_steps 数组
for (iter = 0; iter < op_count; ++iter) {
outer_steps[iter] = (ptrdiff_t) steps[iter];
}
// 更新 steps 指针
steps += op_count;
// 初始化 gesdd 参数
if (init_gesdd(¶ms,
JOBZ,
(fortran_int)dimensions[0],
(fortran_int)dimensions[1],
注:代码截至此处。
template<typename typ>
static void
svd_N(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *NPY_UNUSED(func))
{
/* 定义保存结果的变量 */
linearize_data u_out = {}, s_out = {}, v_out = {};
/* 计算最小的维度大小 */
fortran_int min_m_n = params.M < params.N ? params.M : params.N;
/* 初始化输入矩阵数据 */
linearize_data a_in = init_linearize_data(params.N, params.M, steps[1], steps[0]);
/* 根据 JOBZ 的值决定需要初始化的输出变量 */
if ('N' == params.JOBZ) {
/* 只需要奇异值 */
s_out = init_linearize_data(1, min_m_n, 0, steps[2]);
} else {
fortran_int u_columns, v_rows;
if ('S' == params.JOBZ) {
/* 需要计算全部左奇异向量和右奇异向量 */
u_columns = min_m_n;
v_rows = min_m_n;
} else { /* JOBZ == 'A' */
/* 需要计算所有左右奇异向量 */
u_columns = params.M;
v_rows = params.N;
}
/* 初始化左奇异向量、奇异值、右奇异向量 */
u_out = init_linearize_data(u_columns, params.M, steps[3], steps[2]);
s_out = init_linearize_data(1, min_m_n, 0, steps[4]);
v_out = init_linearize_data(params.N, v_rows, steps[6], steps[5]);
}
/* 外层循环迭代 */
for (iter = 0; iter < outer_dim; ++iter) {
int not_ok;
/* 将矩阵复制到输入矩阵数据结构 */
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
/* 调用 SVD 计算 */
not_ok = call_gesdd(¶ms);
if (!not_ok) {
/* 如果计算成功 */
if ('N' == params.JOBZ) {
/* 只返回奇异值 */
delinearize_matrix((basetyp*)args[1], (basetyp*)params.S, &s_out);
} else {
if ('A' == params.JOBZ && min_m_n == 0) {
/* 处理 Lapack 未初始化的情况,产生单位矩阵 */
identity_matrix((typ*)params.U, params.M);
identity_matrix((typ*)params.VT, params.N);
}
/* 返回左右奇异向量和奇异值 */
delinearize_matrix((typ*)args[1], (typ*)params.U, &u_out);
delinearize_matrix((basetyp*)args[2], (basetyp*)params.S, &s_out);
delinearize_matrix((typ*)args[3], (typ*)params.VT, &v_out);
}
} else {
/* 如果计算失败 */
error_occurred = 1;
if ('N' == params.JOBZ) {
/* 返回 NaN 奇异值 */
nan_matrix((basetyp*)args[1], &s_out);
} else {
/* 返回 NaN 左右奇异向量和奇异值 */
nan_matrix((typ*)args[1], &u_out);
nan_matrix((basetyp*)args[2], &s_out);
nan_matrix((typ*)args[3], &v_out);
}
}
/* 更新指针位置 */
update_pointers((npy_uint8**)args, outer_steps, op_count);
}
/* 释放 SVD 计算资源 */
release_gesdd(¶ms);
}
/* 设置错误标志 */
set_fp_invalid_or_clear(error_occurred);
svd_wrapper<fortran_type_t<typ>>('N', args, dimensions, steps);
/* -------------------------------------------------------------------------- */
/* svd_S: 执行SVD分解的静态函数,处理模板类型typ对应的单字母参数'S' */
template<typename typ>
static void
svd_S(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *NPY_UNUSED(func))
{
// 调用svd_wrapper函数处理SVD分解,传入模板类型typ对应的参数和相关信息
svd_wrapper<fortran_type_t<typ>>('S', args, dimensions, steps);
}
/* svd_A: 执行SVD分解的静态函数,处理模板类型typ对应的单字母参数'A' */
template<typename typ>
static void
svd_A(char **args,
npy_intp const *dimensions,
npy_intp const *steps,
void *NPY_UNUSED(func))
{
// 调用svd_wrapper函数处理SVD分解,传入模板类型typ对应的参数和相关信息
svd_wrapper<fortran_type_t<typ>>('A', args, dimensions, steps);
}
/* -------------------------------------------------------------------------- */
/* qr (modes - r, raw) */
/* GEQRF_PARAMS_t: 用于存储GEQRF参数的结构模板 */
template<typename typ>
struct GEQRF_PARAMS_t
{
fortran_int M; // 矩阵A的行数
fortran_int N; // 矩阵A的列数
typ *A; // 指向矩阵A的指针
fortran_int LDA; // 矩阵A的leading dimension
typ* TAU; // 存储元素的向量tau
typ *WORK; // 存储工作区域的指针
fortran_int LWORK; // 工作区域的大小
};
/* dump_geqrf_params: 打印GEQRF参数的函数 */
template<typename typ>
static inline void
dump_geqrf_params(const char *name,
GEQRF_PARAMS_t<typ> *params)
{
// 使用TRACE_TXT打印GEQRF参数的详细信息,包括矩阵A、向量TAU、工作区等
TRACE_TXT("\n%s:\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n",
name,
"A", params->A,
"TAU", params->TAU,
"WORK", params->WORK,
"M", (int)params->M,
"N", (int)params->N,
"LDA", (int)params->LDA,
"LWORK", (int)params->LWORK);
}
/* call_geqrf: 调用LAPACK库中的dgeqrf函数执行QR分解 */
static inline fortran_int
call_geqrf(GEQRF_PARAMS_t<double> *params)
{
fortran_int rv; // 存储函数返回值
// 调用LAPACK库中的dgeqrf函数执行QR分解
LAPACK(dgeqrf)(¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->TAU,
params->WORK, ¶ms->LWORK,
&rv);
return rv; // 返回函数执行结果
}
/* call_geqrf: 调用LAPACK库中的zgeqrf函数执行复数类型的QR分解 */
static inline fortran_int
call_geqrf(GEQRF_PARAMS_t<f2c_doublecomplex> *params)
{
fortran_int rv; // 存储函数返回值
// 调用LAPACK库中的zgeqrf函数执行复数类型的QR分解
LAPACK(zgeqrf)(¶ms->M, ¶ms->N,
params->A, ¶ms->LDA,
params->TAU,
params->WORK, ¶ms->LWORK,
&rv);
return rv; // 返回函数执行结果
}
/* init_geqrf: 初始化GEQRF参数结构 */
static inline int
init_geqrf(GEQRF_PARAMS_t<fortran_doublereal> *params,
fortran_int m,
fortran_int n)
{
using ftyp = fortran_doublereal;
npy_uint8 *mem_buff = NULL; // 内存缓冲区指针
npy_uint8 *mem_buff2 = NULL; // 第二个内存缓冲区指针
npy_uint8 *a, *tau, *work; // 指向矩阵A、向量TAU、工作区的指针
fortran_int min_m_n = fortran_int_min(m, n); // m和n的最小值
size_t safe_min_m_n = min_m_n; // 安全的最小值
size_t safe_m = m; // 安全的m值
size_t safe_n = n; // 安全的n值
size_t a_size = safe_m * safe_n * sizeof(ftyp); // 计算矩阵A所需内存大小
size_t tau_size = safe_min_m_n * sizeof(ftyp); // 计算向量TAU所需内存大小
fortran_int work_count; // 工作区域计数
size_t work_size; // 工作区域大小
fortran_int lda = fortran_int_max(1, m); // 确定矩阵A的leading dimension
mem_buff = (npy_uint8 *)malloc(a_size + tau_size); // 分配内存空间
if (!mem_buff) // 内存分配失败处理
goto error;
a = mem_buff; // 指向矩阵A的指针
tau = a + a_size; // 指向向量TAU的指针
memset(tau, 0, tau_size); // 将向量TAU的内存清零
// 初始化GEQRF参数结构
params->M = m; // 设置矩阵A的行数
params->N = n; // 设置矩阵A的列数
params->A = (ftyp*)a; // 设置矩阵A的指针
params->TAU = (ftyp*)tau; // 设置向量TAU的指针
params->LDA = lda; // 设置矩阵A的leading dimension
{
/* 计算最佳工作大小 */
// 声明一个变量来存储工作大小查询的结果
ftyp work_size_query;
// 将参数结构体中的 WORK 指针指向工作大小查询的结果变量
params->WORK = &work_size_query;
// 设置参数结构体中的 LWORK 为 -1,用于查询工作大小
params->LWORK = -1;
// 调用函数进行 QR 分解,如果返回值不为 0,则跳转到错误处理标签
if (call_geqrf(params) != 0)
goto error;
// 将工作大小查询结果转换为整数类型并存储在 work_count 中
work_count = (fortran_int) *(ftyp*) params->WORK;
}
// 根据计算得到的工作大小和 n 的最大值来确定最终的 LWORK
params->LWORK = fortran_int_max(fortran_int_max(1, n), work_count);
// 计算所需的内存大小,并分配相应的内存空间
work_size = (size_t) params->LWORK * sizeof(ftyp);
mem_buff2 = (npy_uint8 *)malloc(work_size);
if (!mem_buff2)
goto error;
// 将分配的内存空间的起始地址赋给工作指针
work = mem_buff2;
// 将工作指针的地址转换为特定类型的指针,并存储在参数结构体的 WORK 中
params->WORK = (ftyp*)work;
// 成功完成初始化,返回 1
return 1;
error:
// 如果发生错误,输出跟踪信息
TRACE_TXT("%s failed init\n", __FUNCTION__);
// 释放已分配的内存空间
free(mem_buff);
free(mem_buff2);
// 将参数结构体清零,以便在错误情况下的状态重置
memset(params, 0, sizeof(*params));
// 返回 0,表示初始化失败
return 0;
}
static inline int
init_geqrf(GEQRF_PARAMS_t<fortran_doublecomplex> *params,
fortran_int m,
fortran_int n)
{
// 定义指向 GEQRF 参数结构体的指针,初始化为 NULL
using ftyp = fortran_doublecomplex;
// 初始化内存缓冲区指针
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
npy_uint8 *a, *tau, *work;
// 计算最小的 m 和 n
fortran_int min_m_n = fortran_int_min(m, n);
// 将 min_m_n 转换为 size_t 类型
size_t safe_min_m_n = min_m_n;
// 将 m 和 n 转换为 size_t 类型
size_t safe_m = m;
size_t safe_n = n;
// 计算 A 和 TAU 的大小
size_t a_size = safe_m * safe_n * sizeof(ftyp);
size_t tau_size = safe_min_m_n * sizeof(ftyp);
// 声明变量用于存储工作区的大小和个数
fortran_int work_count;
size_t work_size;
// 计算 lda,确保不小于 1
fortran_int lda = fortran_int_max(1, m);
// 分配内存并检查分配情况
mem_buff = (npy_uint8 *)malloc(a_size + tau_size);
if (!mem_buff)
goto error;
// 设置 a 指针和 tau 指针
a = mem_buff;
tau = a + a_size;
// 将 tau 内存块清零
memset(tau, 0, tau_size);
// 初始化 GEQRF_PARAMS_t 结构体的参数
params->M = m;
params->N = n;
params->A = (ftyp*)a;
params->TAU = (ftyp*)tau;
params->LDA = lda;
{
/* 计算最优工作区大小 */
// 声明一个用于查询工作区大小的变量
ftyp work_size_query;
// 设置 params 的 WORK 指针为 work_size_query 的地址,并设置 LWORK 为 -1
params->WORK = &work_size_query;
params->LWORK = -1;
// 调用 call_geqrf 函数进行计算,并检查返回值
if (call_geqrf(params) != 0)
goto error;
// 将计算得到的工作区大小转换为 fortran_int 类型
work_count = (fortran_int) ((ftyp*)params->WORK)->r;
}
// 设置 LWORK 为 n 和 work_count 中的较大值
params->LWORK = fortran_int_max(fortran_int_max(1, n),
work_count);
// 计算工作区的实际大小
work_size = (size_t) params->LWORK * sizeof(ftyp);
// 分配内存并检查分配情况
mem_buff2 = (npy_uint8 *)malloc(work_size);
if (!mem_buff2)
goto error;
// 设置 work 指针
work = mem_buff2;
// 将 params 的 WORK 指针设置为 work 的地址
params->WORK = (ftyp*)work;
// 返回成功状态
return 1;
error:
// 打印错误消息并释放内存
TRACE_TXT("%s failed init\n", __FUNCTION__);
free(mem_buff);
free(mem_buff2);
// 将 params 结构体清零
memset(params, 0, sizeof(*params));
// 返回失败状态
return 0;
}
template<typename ftyp>
static inline void
release_geqrf(GEQRF_PARAMS_t<ftyp>* params)
{
// 释放 A 和 WORK 所指向的内存块
free(params->A);
free(params->WORK);
// 将 params 结构体清零
memset(params, 0, sizeof(*params));
}
template<typename typ>
static void
qr_r_raw(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
// 定义 ftyp 为 typ 对应的 fortran 类型
using ftyp = fortran_type_t<typ>;
// 声明 GEQRF_PARAMS_t 结构体的变量 params
GEQRF_PARAMS_t<ftyp> params;
// 声明变量用于指示是否发生错误
int error_occurred = get_fp_invalid_and_clear();
// 声明变量 n 和 m
fortran_int n, m;
// 初始化外层循环,由外部定义
INIT_OUTER_LOOP_2
// 从 dimensions 数组中获取 m 和 n
m = (fortran_int)dimensions[0];
n = (fortran_int)dimensions[1];
// 调用 init_geqrf 函数初始化 params,如果初始化成功则执行以下代码块
if (init_geqrf(¶ms, m, n)) {
// 初始化线性化数据结构 a_in 和 tau_out
linearize_data a_in = init_linearize_data(n, m, steps[1], steps[0]);
linearize_data tau_out = init_linearize_data(1, fortran_int_min(m, n), 1, steps[2]);
// 开始外层循环
BEGIN_OUTER_LOOP_2
int not_ok;
// 线性化矩阵数据并调用 GEQRF 函数
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
not_ok = call_geqrf(¶ms);
// 如果执行成功,则反线性化矩阵数据
if (!not_ok) {
delinearize_matrix((typ*)args[0], (typ*)params.A, &a_in);
delinearize_matrix((typ*)args[1], (typ*)params.TAU, &tau_out);
} else {
// 如果执行失败,设置错误标志,并用 NaN 填充矩阵
error_occurred = 1;
nan_matrix((typ*)args[1], &tau_out);
}
// 结束外层循环
END_OUTER_LOOP
// 释放 params 所指向的内存块
release_geqrf(¶ms);
}
}
}
// 这里是一个函数或者代码块的结束
// 没有具体代码行为展示,可能是函数的最后一行或者是一个条件语句的结尾
// 程序员用大括号来标识代码块的开始和结束
// 这里的大括号可能是 if、for、while 或者函数定义的一部分
set_fp_invalid_or_clear(error_occurred);
// 调用函数 set_fp_invalid_or_clear,传递参数 error_occurred
// 函数的具体作用可以根据上下文来理解,它可能用于设置浮点处理器状态为无效或者清除错误标志
// error_occurred 可能是一个布尔变量或者整数,用来指示是否发生了错误
/* -------------------------------------------------------------------------- */
/* qr common code (modes - reduced and complete) */
template<typename typ>
struct GQR_PARAMS_t
{
fortran_int M; // Number of rows in matrix A
fortran_int MC; // Column dimension of the input matrix Q
fortran_int MN; // Minimum of M and N
void* A; // Pointer to matrix A
typ *Q; // Pointer to matrix Q
fortran_int LDA; // Leading dimension of A
typ* TAU; // Scalar factors of the elementary reflectors
typ *WORK; // Workspace
fortran_int LWORK; // Length of the workspace array
} ;
static inline fortran_int
call_gqr(GQR_PARAMS_t<double> *params)
{
fortran_int rv;
LAPACK(dorgqr)(¶ms->M, ¶ms->MC, ¶ms->MN,
params->Q, ¶ms->LDA,
params->TAU,
params->WORK, ¶ms->LWORK,
&rv);
return rv;
}
static inline fortran_int
call_gqr(GQR_PARAMS_t<f2c_doublecomplex> *params)
{
fortran_int rv;
LAPACK(zungqr)(¶ms->M, ¶ms->MC, ¶ms->MN,
params->Q, ¶ms->LDA,
params->TAU,
params->WORK, ¶ms->LWORK,
&rv);
return rv;
}
static inline int
init_gqr_common(GQR_PARAMS_t<fortran_doublereal> *params,
fortran_int m,
fortran_int n,
fortran_int mc)
{
using ftyp = fortran_doublereal;
npy_uint8 *mem_buff = NULL; // Memory buffer for workspace and matrices
npy_uint8 *mem_buff2 = NULL; // Additional memory buffer for workspace
npy_uint8 *a, *q, *tau, *work; // Pointers to different parts of mem_buff
fortran_int min_m_n = fortran_int_min(m, n); // Minimum of M and N
size_t safe_mc = mc; // Column dimension of Q, safely cast to size_t
size_t safe_min_m_n = min_m_n; // Safely cast min_m_n to size_t
size_t safe_m = m; // Safely cast m to size_t
size_t safe_n = n; // Safely cast n to size_t
size_t a_size = safe_m * safe_n * sizeof(ftyp); // Size of matrix A in bytes
size_t q_size = safe_m * safe_mc * sizeof(ftyp); // Size of matrix Q in bytes
size_t tau_size = safe_min_m_n * sizeof(ftyp); // Size of tau vector in bytes
fortran_int work_count; // Size of workspace as returned by LAPACK
size_t work_size; // Size of workspace in bytes
fortran_int lda = fortran_int_max(1, m); // Leading dimension of A
mem_buff = (npy_uint8 *)malloc(q_size + tau_size + a_size); // Allocate memory for Q, tau, and A
if (!mem_buff)
goto error;
q = mem_buff; // Q starts at the beginning of mem_buff
tau = q + q_size; // tau follows Q in memory
a = tau + tau_size; // A follows tau in memory
params->M = m; // Set struct members based on input parameters
params->MC = mc;
params->MN = min_m_n;
params->A = a;
params->Q = (ftyp*)q;
params->TAU = (ftyp*)tau;
params->LDA = lda;
{
/* compute optimal work size */
ftyp work_size_query; // Temporary variable to query workspace size
params->WORK = &work_size_query; // Set WORK to point to work_size_query
params->LWORK = -1; // Set LWORK to -1 to query optimal workspace size
if (call_gqr(params) != 0) // Call LAPACK function to query workspace size
goto error;
work_count = (fortran_int) *(ftyp*) params->WORK; // Extract workspace size from WORK
}
params->LWORK = fortran_int_max(fortran_int_max(1, n), work_count); // Set LWORK to the maximum needed workspace size
work_size = (size_t) params->LWORK * sizeof(ftyp); // Calculate total workspace size in bytes
mem_buff2 = (npy_uint8 *)malloc(work_size); // Allocate additional memory for workspace
if (!mem_buff2)
goto error;
work = mem_buff2; // Set work pointer to point to the allocated workspace
params->WORK = (ftyp*)work; // Set params->WORK to point to the allocated workspace
return 1; // Return success
error:
TRACE_TXT("%s failed init\n", __FUNCTION__); // Log initialization failure
free(mem_buff); // Free memory buffers
free(mem_buff2);
memset(params, 0, sizeof(*params)); // Clear params structure
return 0; // Return failure
}
/*
初始化通用 GQR 参数,使用指定的参数和尺寸。
参数说明:
- params: GQR 参数结构体指针,用于存储初始化后的参数
- m: 矩阵维度 m
- n: 矩阵维度 n
- mc: 矩阵列维度,通常为 m 和 n 中较小的一个
返回值:
- 返回 1 表示初始化成功,返回 0 表示初始化失败
注意事项:
- 函数内部会动态分配内存,需要在使用后手动释放以避免内存泄漏
- 初始化过程中若调用 call_gqr 函数失败会立即返回并释放之前分配的内存
*/
init_gqr_common(GQR_PARAMS_t<fortran_doublecomplex> *params,
fortran_int m,
fortran_int n,
fortran_int mc)
{
using ftyp=fortran_doublecomplex;
npy_uint8 *mem_buff = NULL; // 初始化内存缓冲区指针
npy_uint8 *mem_buff2 = NULL; // 初始化第二个内存缓冲区指针
npy_uint8 *a, *q, *tau, *work; // 定义指向不同数据区域的指针
fortran_int min_m_n = fortran_int_min(m, n); // 计算 m 和 n 中的较小值
size_t safe_mc = mc; // 将 mc 转为 size_t 类型的安全变量
size_t safe_min_m_n = min_m_n; // 将 min_m_n 转为 size_t 类型的安全变量
size_t safe_m = m; // 将 m 转为 size_t 类型的安全变量
size_t safe_n = n; // 将 n 转为 size_t 类型的安全变量
size_t a_size = safe_m * safe_n * sizeof(ftyp); // 计算 A 数据区域的大小
size_t q_size = safe_m * safe_mc * sizeof(ftyp); // 计算 Q 数据区域的大小
size_t tau_size = safe_min_m_n * sizeof(ftyp); // 计算 TAU 数据区域的大小
fortran_int work_count; // 工作区域计数变量
size_t work_size; // 工作区域的大小变量
fortran_int lda = fortran_int_max(1, m); // 计算 lda,即 m 和 1 中的较大值
mem_buff = (npy_uint8 *)malloc(q_size + tau_size + a_size); // 分配内存用于 Q、TAU 和 A 数据区域
if (!mem_buff)
goto error; // 内存分配失败,跳转到 error 标签处
q = mem_buff; // 设置 Q 指针
tau = q + q_size; // 设置 TAU 指针
a = tau + tau_size; // 设置 A 指针
params->M = m; // 设置参数结构体中的 M 字段
params->MC = mc; // 设置参数结构体中的 MC 字段
params->MN = min_m_n; // 设置参数结构体中的 MN 字段
params->A = a; // 设置参数结构体中的 A 指针
params->Q = (ftyp*)q; // 设置参数结构体中的 Q 指针
params->TAU = (ftyp*)tau; // 设置参数结构体中的 TAU 指针
params->LDA = lda; // 设置参数结构体中的 LDA 字段
{
/* 计算最优工作区域大小 */
ftyp work_size_query;
params->WORK = &work_size_query; // 设置 WORK 指针为工作区域大小查询变量的地址
params->LWORK = -1; // 设置 LWORK 为 -1,表示进行大小查询
if (call_gqr(params) != 0) // 调用 call_gqr 函数进行工作区域大小查询,如果失败则跳转到 error 标签处
goto error;
work_count = (fortran_int) ((ftyp*)params->WORK)->r; // 获取工作区域的大小
}
params->LWORK = fortran_int_max(fortran_int_max(1, n),
work_count); // 设置 LWORK 为 n 和工作区域大小中的较大值
work_size = (size_t) params->LWORK * sizeof(ftyp); // 计算实际工作区域的大小
mem_buff2 = (npy_uint8 *)malloc(work_size); // 分配内存用于工作区域
if (!mem_buff2)
goto error; // 内存分配失败,跳转到 error 标签处
work = mem_buff2; // 设置工作区域的指针
params->WORK = (ftyp*)work; // 设置参数结构体中的 WORK 指针为工作区域的地址
params->LWORK = work_count; // 设置参数结构体中的 LWORK 为实际工作区域的大小
return 1; // 初始化成功,返回 1
error:
TRACE_TXT("%s failed init\n", __FUNCTION__); // 输出初始化失败的错误信息
free(mem_buff); // 释放分配的内存
free(mem_buff2); // 释放分配的内存
memset(params, 0, sizeof(*params)); // 将 params 结构体清零
return 0; // 初始化失败,返回 0
}
/* -------------------------------------------------------------------------- */
/* qr (modes - reduced) */
/*
以文本形式输出 GQR 参数结构体中的内容。
参数说明:
- name: 输出的名称字符串
- params: GQR 参数结构体指针,包含要输出的数据
注意事项:
- 输出格式固定,包含 Q、TAU、WORK、M、MC、MN、LDA、LWORK 等信息
*/
template<typename typ>
static inline void
dump_gqr_params(const char *name,
GQR_PARAMS_t<typ> *params)
{
TRACE_TXT("\n%s:\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n",
name,
"Q", params->Q,
"TAU", params->TAU,
"WORK", params->WORK,
"M", (int)params->M,
"MC", (int)params->MC,
"MN", (int)params->MN,
"LDA", (int)params->LDA,
"LWORK", (int)params->LWORK);
}
/*
初始化 GQR 参数结构体,简化接口。
参数说明:
- params: GQR 参数结构体指针,用于存储初始化后的参数
- m: 矩阵维度 m
- n: 矩阵维度 n
返回值:
- 返回初始化结果,调用 init_gqr_common 函数进行初始化
*/
template<typename ftyp>
static inline int
init_gqr(GQR_PARAMS_t<ftyp> *params,
fortran_int m,
fortran_int n)
{
return init_gqr_common(
params, m, n,
fortran_int_min(m, n)); // 调用通用初始化函数,并传入 m、n 和它们的最小值作为参数
}
/* 释放 GQR_PARAMS_t 结构体中动态分配的 Q 和 WORK 内存块,并将 params 结构体清零 */
release_gqr(GQR_PARAMS_t<typ>* params)
{
free(params->Q); // 释放 Q 指向的内存块
free(params->WORK); // 释放 WORK 指向的内存块
memset(params, 0, sizeof(*params)); // 将 params 结构体清零
}
/* 使用模板 typename typ 实例化的静态函数,执行 QR 分解 */
template<typename typ>
static void
qr_reduced(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
using ftyp = fortran_type_t<typ>; // 定义 fortran 类型别名 ftyp
GQR_PARAMS_t<ftyp> params; // 声明 GQR_PARAMS_t 结构体对象 params
int error_occurred = get_fp_invalid_and_clear(); // 获取并清除浮点错误标志
fortran_int n, m; // 声明 fortran 整型变量 n 和 m
INIT_OUTER_LOOP_3 // 宏定义的外部循环初始化
m = (fortran_int)dimensions[0]; // 从 dimensions 数组获取 m
n = (fortran_int)dimensions[1]; // 从 dimensions 数组获取 n
// 如果初始化 GQR_PARAMS_t 结构体失败,则跳过 QR 分解
if (init_gqr(¶ms, m, n)) {
// 初始化线性化数据结构
linearize_data a_in = init_linearize_data(n, m, steps[1], steps[0]);
linearize_data tau_in = init_linearize_data(1, fortran_int_min(m, n), 1, steps[2]);
linearize_data q_out = init_linearize_data(fortran_int_min(m, n), m, steps[4], steps[3]);
BEGIN_OUTER_LOOP_3 // 外部循环开始
int not_ok; // 定义整型变量 not_ok
// 将 args[0] 中的数据线性化到 params.A 中
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
// 将 args[0] 中的数据线性化到 params.Q 中
linearize_matrix((typ*)params.Q, (typ*)args[0], &a_in);
// 将 args[1] 中的数据线性化到 params.TAU 中
linearize_matrix((typ*)params.TAU, (typ*)args[1], &tau_in);
// 调用 GQR 过程,将结果保存到 not_ok 中
not_ok = call_gqr(¶ms);
// 如果 GQR 过程执行成功
if (!not_ok) {
// 将 params.Q 的结果反线性化到 args[2] 中
delinearize_matrix((typ*)args[2], (typ*)params.Q, &q_out);
} else {
// 如果 GQR 过程执行失败,标记错误,并将 args[2] 矩阵置为 NaN
error_occurred = 1;
nan_matrix((typ*)args[2], &q_out);
}
END_OUTER_LOOP // 外部循环结束
// 释放 GQR_PARAMS_t 结构体中的资源
release_gqr(¶ms);
}
// 设置浮点错误标志
set_fp_invalid_or_clear(error_occurred);
}
/* 初始化 GQR_PARAMS_t 结构体用于 QR 完整模式 */
template<typename ftyp>
static inline int
init_gqr_complete(GQR_PARAMS_t<ftyp> *params,
fortran_int m,
fortran_int n)
{
return init_gqr_common(params, m, n, m); // 调用通用初始化函数
}
/* QR 完整模式的模板函数 */
template<typename typ>
static void
qr_complete(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
using ftyp = fortran_type_t<typ>; // 定义 fortran 类型别名 ftyp
GQR_PARAMS_t<ftyp> params; // 声明 GQR_PARAMS_t 结构体对象 params
int error_occurred = get_fp_invalid_and_clear(); // 获取并清除浮点错误标志
fortran_int n, m; // 声明 fortran 整型变量 n 和 m
INIT_OUTER_LOOP_3 // 宏定义的外部循环初始化
m = (fortran_int)dimensions[0]; // 从 dimensions 数组获取 m
n = (fortran_int)dimensions[1]; // 从 dimensions 数组获取 n
if (init_gqr_complete(¶ms, m, n)) {
linearize_data a_in = init_linearize_data(n, m, steps[1], steps[0]);
linearize_data tau_in = init_linearize_data(1, fortran_int_min(m, n), 1, steps[2]);
linearize_data q_out = init_linearize_data(m, m, steps[4], steps[3]);
BEGIN_OUTER_LOOP_3
int not_ok;
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
linearize_matrix((typ*)params.Q, (typ*)args[0], &a_in);
linearize_matrix((typ*)params.TAU, (typ*)args[1], &tau_in);
not_ok = call_gqr(¶ms);
if (!not_ok) {
delinearize_matrix((typ*)args[2], (typ*)params.Q, &q_out);
} else {
error_occurred = 1;
nan_matrix((typ*)args[2], &q_out);
}
END_OUTER_LOOP
release_gqr(¶ms);
}
set_fp_invalid_or_clear(error_occurred);
/* -------------------------------------------------------------------------- */
/* least squares */
/* 定义模板结构体 GELSD_PARAMS_t,用于存储调用最小二乘求解函数参数 */
template<typename typ>
struct GELSD_PARAMS_t
{
fortran_int M; // 行数
fortran_int N; // 列数
fortran_int NRHS; // 右侧矩阵的列数
typ *A; // 输入矩阵 A
fortran_int LDA; // A 的列数
typ *B; // 输出矩阵 B
fortran_int LDB; // B 的列数
basetype_t<typ> *S; // 奇异值数组
basetype_t<typ> *RCOND;// 条件数的阈值
fortran_int RANK; // 矩阵的秩
typ *WORK; // 工作空间数组
fortran_int LWORK; // 工作空间的大小
basetype_t<typ> *RWORK;// 实数工作空间
fortran_int *IWORK; // 整数工作空间
};
/* 打印输出 GELSD_PARAMS_t 结构体的内容 */
template<typename typ>
static inline void
dump_gelsd_params(const char *name,
GELSD_PARAMS_t<typ> *params)
{
TRACE_TXT("\n%s:\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18p\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18d\n"\
"%14s: %18p\n",
name,
"A", params->A,
"B", params->B,
"S", params->S,
"WORK", params->WORK,
"RWORK", params->RWORK,
"IWORK", params->IWORK,
"M", (int)params->M,
"N", (int)params->N,
"NRHS", (int)params->NRHS,
"LDA", (int)params->LDA,
"LDB", (int)params->LDB,
"LWORK", (int)params->LWORK,
"RANK", (int)params->RANK,
"RCOND", params->RCOND);
}
/* 调用 LAPACK 库中的 sgelsd 函数解决最小二乘问题(单精度实数版本) */
static inline fortran_int
call_gelsd(GELSD_PARAMS_t<fortran_real> *params)
{
fortran_int rv;
LAPACK(sgelsd)(¶ms->M, ¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->B, ¶ms->LDB,
params->S,
params->RCOND, ¶ms->RANK,
params->WORK, ¶ms->LWORK,
params->IWORK,
&rv);
return rv;
}
/* 调用 LAPACK 库中的 dgelsd 函数解决最小二乘问题(双精度实数版本) */
static inline fortran_int
call_gelsd(GELSD_PARAMS_t<fortran_doublereal> *params)
{
fortran_int rv;
LAPACK(dgelsd)(¶ms->M, ¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->B, ¶ms->LDB,
params->S,
params->RCOND, ¶ms->RANK,
params->WORK, ¶ms->LWORK,
params->IWORK,
&rv);
return rv;
}
/* 初始化 GELSD_PARAMS_t 结构体,准备调用最小二乘求解函数 */
template<typename ftyp>
static inline int
init_gelsd(GELSD_PARAMS_t<ftyp> *params,
fortran_int m,
fortran_int n,
fortran_int nrhs,
scalar_trait)
{
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
npy_uint8 *a, *b, *s, *work, *iwork;
fortran_int min_m_n = fortran_int_min(m, n);
fortran_int max_m_n = fortran_int_max(m, n);
size_t safe_min_m_n = min_m_n;
size_t safe_max_m_n = max_m_n;
size_t safe_m = m;
size_t safe_n = n;
size_t safe_nrhs = nrhs;
size_t a_size = safe_m * safe_n * sizeof(ftyp);
size_t b_size = safe_max_m_n * safe_nrhs * sizeof(ftyp);
size_t s_size = safe_min_m_n * sizeof(ftyp);
fortran_int work_count;
size_t work_size;
size_t iwork_size;
fortran_int lda = fortran_int_max(1, m);
fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n));
size_t msize = a_size + b_size + s_size;
mem_buff = (npy_uint8 *)malloc(msize != 0 ? msize : 1);
if (!mem_buff) {
goto no_memory;
}
a = mem_buff;
b = a + a_size;
s = b + b_size;
params->M = m;
params->N = n;
params->NRHS = nrhs;
params->A = (ftyp*)a;
params->B = (ftyp*)b;
params->S = (ftyp*)s;
params->LDA = lda;
params->LDB = ldb;
{
/* 计算最优工作区大小 */
ftyp work_size_query;
fortran_int iwork_size_query;
params->WORK = &work_size_query;
params->IWORK = &iwork_size_query;
params->RWORK = NULL;
params->LWORK = -1;
if (call_gelsd(params) != 0) {
goto error;
}
work_count = (fortran_int)work_size_query;
work_size = (size_t) work_size_query * sizeof(ftyp);
iwork_size = (size_t)iwork_size_query * sizeof(fortran_int);
}
mem_buff2 = (npy_uint8 *)malloc(work_size + iwork_size);
if (!mem_buff2) {
goto no_memory;
}
work = mem_buff2;
iwork = work + work_size;
params->WORK = (ftyp*)work;
params->RWORK = NULL;
params->IWORK = (fortran_int*)iwork;
params->LWORK = work_count;
return 1;
no_memory:
NPY_ALLOW_C_API_DEF
NPY_ALLOW_C_API;
PyErr_NoMemory();
NPY_DISABLE_C_API;
error:
TRACE_TXT("%s failed init\n", __FUNCTION__);
free(mem_buff);
free(mem_buff2);
memset(params, 0, sizeof(*params));
return 0;
}
static inline fortran_int
call_gelsd(GELSD_PARAMS_t<fortran_complex> *params)
{
// 声明返回值变量
fortran_int rv;
// 调用 LAPACK 库中的复数版本的 GELSD 函数
LAPACK(cgelsd)(¶ms->M, ¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->B, ¶ms->LDB,
params->S,
params->RCOND, ¶ms->RANK,
params->WORK, ¶ms->LWORK,
params->RWORK, (fortran_int*)params->IWORK,
&rv);
// 返回 LAPACK 函数的返回值
return rv;
}
static inline fortran_int
call_gelsd(GELSD_PARAMS_t<fortran_doublecomplex> *params)
{
// 声明返回值变量
fortran_int rv;
// 调用 LAPACK 库中的双精度复数版本的 GELSD 函数
LAPACK(zgelsd)(¶ms->M, ¶ms->N, ¶ms->NRHS,
params->A, ¶ms->LDA,
params->B, ¶ms->LDB,
params->S,
params->RCOND, ¶ms->RANK,
params->WORK, ¶ms->LWORK,
params->RWORK, (fortran_int*)params->IWORK,
&rv);
// 返回 LAPACK 函数的返回值
return rv;
}
template<typename ftyp>
static inline int
init_gelsd(GELSD_PARAMS_t<ftyp> *params,
fortran_int m,
fortran_int n,
fortran_int nrhs,
complex_trait)
{
// 使用模板类型确定实数类型
using frealtyp = basetype_t<ftyp>;
// 初始化指针变量
npy_uint8 *mem_buff = NULL;
npy_uint8 *mem_buff2 = NULL;
npy_uint8 *a, *b, *s, *work, *iwork, *rwork;
// 计算最小值和最大值
fortran_int min_m_n = fortran_int_min(m, n);
fortran_int max_m_n = fortran_int_max(m, n);
// 安全转换为大小类型
size_t safe_min_m_n = min_m_n;
size_t safe_max_m_n = max_m_n;
size_t safe_m = m;
size_t safe_n = n;
size_t safe_nrhs = nrhs;
// 计算各个缓冲区的大小
size_t a_size = safe_m * safe_n * sizeof(ftyp);
size_t b_size = safe_max_m_n * safe_nrhs * sizeof(ftyp);
size_t s_size = safe_min_m_n * sizeof(frealtyp);
// 初始化工作变量的计数和大小
fortran_int work_count;
size_t work_size, rwork_size, iwork_size;
// 计算 LDA 和 LDB 的值
fortran_int lda = fortran_int_max(1, m);
fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n));
// 计算总内存大小并分配内存
size_t msize = a_size + b_size + s_size;
mem_buff = (npy_uint8 *)malloc(msize != 0 ? msize : 1);
// 内存分配失败处理
if (!mem_buff) {
goto no_memory;
}
// 设置缓冲区指针
a = mem_buff;
b = a + a_size;
s = b + b_size;
// 初始化参数结构体的各个字段
params->M = m;
params->N = n;
params->NRHS = nrhs;
params->A = (ftyp*)a;
params->B = (ftyp*)b;
params->S = (frealtyp*)s;
params->LDA = lda;
params->LDB = ldb;
{
/* 计算最优工作大小 */
ftyp work_size_query; // 定义工作大小查询变量
frealtyp rwork_size_query; // 定义实数工作大小查询变量
fortran_int iwork_size_query; // 定义整数工作大小查询变量
params->WORK = &work_size_query; // 设置参数结构体中的 WORK 指针
params->IWORK = &iwork_size_query; // 设置参数结构体中的 IWORK 指针
params->RWORK = &rwork_size_query; // 设置参数结构体中的 RWORK 指针
params->LWORK = -1; // 设置工作大小为 -1,用于查询最优工作大小
if (call_gelsd(params) != 0) { // 调用函数查询最优工作大小,如果不成功则跳转到错误处理
goto error;
}
work_count = (fortran_int)work_size_query.r; // 将查询到的工作大小转换为整数存储
work_size = (size_t )work_size_query.r * sizeof(ftyp); // 计算工作大小的内存空间大小
rwork_size = (size_t)rwork_size_query * sizeof(frealtyp); // 计算实数工作大小的内存空间大小
iwork_size = (size_t)iwork_size_query * sizeof(fortran_int); // 计算整数工作大小的内存空间大小
}
mem_buff2 = (npy_uint8 *)malloc(work_size + rwork_size + iwork_size); // 分配工作空间内存
if (!mem_buff2) { // 如果内存分配失败,则跳转到无内存处理
goto no_memory;
}
work = mem_buff2; // 将分配的内存空间指针赋值给工作指针
rwork = work + work_size; // 计算实数工作空间指针
iwork = rwork + rwork_size; // 计算整数工作空间指针
params->WORK = (ftyp*)work; // 设置参数结构体中的 WORK 指针
params->RWORK = (frealtyp*)rwork; // 设置参数结构体中的 RWORK 指针
params->IWORK = (fortran_int*)iwork; // 设置参数结构体中的 IWORK 指针
params->LWORK = work_count; // 设置工作大小为查询到的最优工作大小
return 1; // 返回成功标识
no_memory:
NPY_ALLOW_C_API_DEF // 允许调用C API宏定义
NPY_ALLOW_C_API; // 允许调用C API
PyErr_NoMemory(); // 报告内存不足错误
NPY_DISABLE_C_API; // 禁用调用C API
error:
TRACE_TXT("%s failed init\n", __FUNCTION__); // 输出错误信息到日志中
free(mem_buff); // 释放之前分配的内存
free(mem_buff2); // 释放工作空间内存
memset(params, 0, sizeof(*params)); // 将参数结构体的内容清零
return 0; // 返回失败标识
}
/**
* 释放 GELSD 参数结构体中的内存资源
* @param params GELSD 参数结构体指针
*/
template<typename ftyp>
static inline void
release_gelsd(GELSD_PARAMS_t<ftyp>* params)
{
/* A and WORK contain allocated blocks */
// 释放 params 结构体中 A 成员指向的内存块
free(params->A);
// 释放 params 结构体中 WORK 成员指向的内存块
free(params->WORK);
// 将 params 结构体清零,大小为其本身的大小
memset(params, 0, sizeof(*params));
}
/**
* 计算连续向量的平方 L2 范数
* @tparam typ 向量元素类型
* @param p 指向向量的指针
* @param n 向量的长度
* @param scalar_trait 标量特性类型
* @return 返回平方 L2 范数的结果
*/
template<typename typ>
static basetype_t<typ>
abs2(typ *p, npy_intp n, scalar_trait) {
npy_intp i;
basetype_t<typ> res = 0;
for (i = 0; i < n; i++) {
typ el = p[i];
res += el*el;
}
return res;
}
/**
* 计算复数向量的平方 L2 范数
* @tparam typ 向量元素类型
* @param p 指向向量的指针
* @param n 向量的长度
* @param complex_trait 复数特性类型
* @return 返回平方 L2 范数的结果
*/
template<typename typ>
static basetype_t<typ>
abs2(typ *p, npy_intp n, complex_trait) {
npy_intp i;
basetype_t<typ> res = 0;
for (i = 0; i < n; i++) {
typ el = p[i];
res += RE(&el)*RE(&el) + IM(&el)*IM(&el);
}
return res;
}
/**
* 最小二乘法求解函数
* @tparam typ 求解的数据类型
* @param args 参数列表
* @param dimensions 数组维度信息
* @param steps 步幅信息
* @param NPY_UNUSED(func) 未使用的函数参数
*/
template<typename typ>
static void
lstsq(char **args, npy_intp const *dimensions, npy_intp const *steps,
void *NPY_UNUSED(func))
{
using ftyp = fortran_type_t<typ>;
using basetyp = basetype_t<typ>;
GELSD_PARAMS_t<ftyp> params;
int error_occurred = get_fp_invalid_and_clear();
fortran_int n, m, nrhs;
fortran_int excess;
INIT_OUTER_LOOP_7
// 获取维度信息
m = (fortran_int)dimensions[0];
n = (fortran_int)dimensions[1];
nrhs = (fortran_int)dimensions[2];
excess = m - n;
// 初始化 GELSD 参数结构体
if (init_gelsd(¶ms, m, n, nrhs, dispatch_scalar<ftyp>{})) {
// 初始化线性化数据结构体
linearize_data a_in = init_linearize_data(n, m, steps[1], steps[0]);
linearize_data b_in = init_linearize_data_ex(nrhs, m, steps[3], steps[2], fortran_int_max(n, m));
linearize_data x_out = init_linearize_data_ex(nrhs, n, steps[5], steps[4], fortran_int_max(n, m));
linearize_data r_out = init_linearize_data(1, nrhs, 1, steps[6]);
linearize_data s_out = init_linearize_data(1, fortran_int_min(n, m), 1, steps[7]);
BEGIN_OUTER_LOOP_7
int not_ok;
// 线性化矩阵数据
linearize_matrix((typ*)params.A, (typ*)args[0], &a_in);
linearize_matrix((typ*)params.B, (typ*)args[1], &b_in);
// 设置 RCOND 参数
params.RCOND = (basetyp*)args[2];
// 调用 GELSD 函数
not_ok = call_gelsd(¶ms);
if (!not_ok) {
// 反线性化矩阵数据
delinearize_matrix((typ*)args[3], (typ*)params.B, &x_out);
*(npy_int*) args[5] = params.RANK;
delinearize_matrix((basetyp*)args[6], (basetyp*)params.S, &s_out);
/* Note that linalg.lstsq discards this when excess == 0 */
// 当 excess >= 0 且 params.RANK == n 时,计算残差的平方和
if (excess >= 0 && params.RANK == n) {
/* Compute the residuals as the square sum of each column */
int i;
char *resid = args[4];
ftyp *components = (ftyp *)params.B + n;
for (i = 0; i < nrhs; i++) {
ftyp *vector = components + i*m;
/* Numpy and fortran floating types are the same size,
* so this cast is safe */
// 计算绝对值的平方
basetyp abs = abs2((typ *)vector, excess,
/* -------------------------------------------------------------------------- */
/* gufunc registration */
static void *array_of_nulls[] = {
(void *)NULL, // 初始化一个包含空指针的数组,长度为16,用于后续的初始化
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL,
(void *)NULL
};
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
FLOAT_
DOUBLE_
}
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
FLOAT_
DOUBLE_
CFLOAT_
CDOUBLE_
}
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
NAME<npy_float, npy_float>, // 定义一个包含不同模板化函数指针的数组,用于注册 gufunc 的实数和复数版本
NAME<npy_double, npy_double>, // 使用不同的模板参数进行实例化,如 npy_float 和 npy_double
NAME<npy_cfloat, npy_float>,
NAME<npy_cdouble, npy_double>
}
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
NAME<npy_float>, // 定义一个包含不同模板化函数指针的数组,用于注册 gufunc 的实数和复数版本
NAME<npy_double>, // 使用不同的模板参数进行实例化,如 npy_float 和 npy_double
NAME<npy_cfloat>,
NAME<npy_cdouble>
}
/* There are problems with eig in complex single precision.
* That kernel is disabled
*/
/* 定义一个宏,用于生成通用函数数组,该数组包含不同数据类型的函数指针 */
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
NAME<fortran_complex,fortran_real>, \ // 为NAME宏展开创建一个函数指针,处理复数到实数
NAME<fortran_doublecomplex,fortran_doublereal>, \ // 为NAME宏展开创建一个函数指针,处理双精度复数到双精度实数
NAME<fortran_doublecomplex,fortran_doublecomplex> \ // 为NAME宏展开创建一个函数指针,处理双精度复数到双精度复数
}
/* 单精度函数不会被使用,因为Python中的输入数据会被提升为双精度,
* 所以这里没有实现它们。
*/
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
DOUBLE_
CDOUBLE_
}
static PyUFuncGenericFunction \
FUNC_ARRAY_NAME(NAME)[] = { \
NAME<npy_double>, \ // 为NAME宏展开创建一个函数指针,处理npy_double类型的数据
NAME<npy_cdouble> \ // 为NAME宏展开创建一个函数指针,处理npy_cdouble类型的数据
}
/* 下面是一系列宏的展开,为不同的数学函数名称(NAME)创建通用函数数组 */
GUFUNC_FUNC_ARRAY_REAL_COMPLEX_(slogdet); // 创建slogdet函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX_(det); // 创建det函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(eighlo); // 创建eighlo函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(eighup); // 创建eighup函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(eigvalshlo); // 创建eigvalshlo函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(eigvalshup); // 创建eigvalshup函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(solve); // 创建solve函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(solve1); // 创建solve1函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(inv); // 创建inv函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(cholesky_lo); // 创建cholesky_lo函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(cholesky_up); // 创建cholesky_up函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(svd_N); // 创建svd_N函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(svd_S); // 创建svd_S函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(svd_A); // 创建svd_A函数的通用函数数组
GUFUNC_FUNC_ARRAY_QR__(qr_r_raw); // 创建qr_r_raw函数的通用函数数组
GUFUNC_FUNC_ARRAY_QR__(qr_reduced); // 创建qr_reduced函数的通用函数数组
GUFUNC_FUNC_ARRAY_QR__(qr_complete); // 创建qr_complete函数的通用函数数组
GUFUNC_FUNC_ARRAY_REAL_COMPLEX__(lstsq); // 创建lstsq函数的通用函数数组
GUFUNC_FUNC_ARRAY_EIG(eig); // 创建eig函数的通用函数数组
GUFUNC_FUNC_ARRAY_EIG(eigvals); // 创建eigvals函数的通用函数数组
/* 定义常量数组,指定不同函数所接受的输入数据类型 */
static const char equal_2_types[] = {
NPY_FLOAT, NPY_FLOAT, // 两个浮点数
NPY_DOUBLE, NPY_DOUBLE, // 两个双精度浮点数
NPY_CFLOAT, NPY_CFLOAT, // 两个复数
NPY_CDOUBLE, NPY_CDOUBLE // 两个双精度复数
};
static const char equal_3_types[] = {
NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, // 三个浮点数
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, // 三个双精度浮点数
NPY_CFLOAT, NPY_CFLOAT, NPY_CFLOAT, // 三个复数
NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE // 三个双精度复数
};
/* 第二个结果是logdet,它总是一个实数 */
static const char slogdet_types[] = {
NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, // 三个浮点数
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, // 三个双精度浮点数
NPY_CFLOAT, NPY_CFLOAT, NPY_FLOAT, // 两个复数和一个浮点数(实部为浮点数)
NPY_CDOUBLE, NPY_CDOUBLE, NPY_DOUBLE // 两个双精度复数和一个双精度浮点数(实部为双精度浮点数)
};
static const char eigh_types[] = {
NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, // 三个浮点数
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, // 三个双精度浮点数
NPY_CFLOAT, NPY_FLOAT, NPY_CFLOAT, // 两个复数和一个浮点数(实部为浮点数)
NPY_CDOUBLE, NPY_DOUBLE, NPY_CDOUBLE // 两个双精度复数和一个双精度浮点数(实部为双精度浮点数)
};
static const char eighvals_types[] = {
NPY_FLOAT, NPY_FLOAT, // 两个浮点数
NPY_DOUBLE, NPY_DOUBLE, // 两个双精度浮点数
NPY_CFLOAT, NPY_FLOAT, // 一个复数和一个浮点数(实部为浮点数)
NPY_CDOUBLE, NPY_DOUBLE // 一个双精度复数和一个双精度浮点数(实部为双精度浮点数)
};
static const char eig_types[] = {
/* 省略了该数组的展开,需要根据实际情况补充 */
};
NPY_FLOAT, NPY_CFLOAT, NPY_CFLOAT,
NPY_DOUBLE, NPY_CDOUBLE, NPY_CDOUBLE,
NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE
};
// 定义一个静态常量数组,包含特征值操作的数据类型
static const char eigvals_types[] = {
NPY_FLOAT, NPY_CFLOAT,
NPY_DOUBLE, NPY_CDOUBLE,
NPY_CDOUBLE, NPY_CDOUBLE
};
// 定义一个静态常量数组,包含 SVD 分解中形状为 (1,1) 的操作的数据类型
static const char svd_1_1_types[] = {
NPY_FLOAT, NPY_FLOAT,
NPY_DOUBLE, NPY_DOUBLE,
NPY_CFLOAT, NPY_FLOAT,
NPY_CDOUBLE, NPY_DOUBLE
};
// 定义一个静态常量数组,包含 SVD 分解中形状为 (1,3) 的操作的数据类型
static const char svd_1_3_types[] = {
NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT,
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE,
NPY_CFLOAT, NPY_CFLOAT, NPY_FLOAT, NPY_CFLOAT,
NPY_CDOUBLE, NPY_CDOUBLE, NPY_DOUBLE, NPY_CDOUBLE
};
// 定义一个静态常量数组,包含 QR 分解中形状为 (raw) 的操作的数据类型
/* A, tau */
static const char qr_r_raw_types[] = {
NPY_DOUBLE, NPY_DOUBLE,
NPY_CDOUBLE, NPY_CDOUBLE,
};
// 定义一个静态常量数组,包含 QR 分解中形状为 (reduced) 的操作的数据类型
/* A, tau, q */
static const char qr_reduced_types[] = {
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE,
NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE,
};
// 定义一个静态常量数组,包含 QR 分解中形状为 (complete) 的操作的数据类型
/* A, tau, q */
static const char qr_complete_types[] = {
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE,
NPY_CDOUBLE, NPY_CDOUBLE, NPY_CDOUBLE,
};
// 定义一个静态常量数组,包含最小二乘解法的数据类型
/* A, b, rcond, x, resid, rank, s, */
static const char lstsq_types[] = {
NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_INT, NPY_FLOAT,
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_INT, NPY_DOUBLE,
NPY_CFLOAT, NPY_CFLOAT, NPY_FLOAT, NPY_CFLOAT, NPY_FLOAT, NPY_INT, NPY_FLOAT,
NPY_CDOUBLE, NPY_CDOUBLE, NPY_DOUBLE, NPY_CDOUBLE, NPY_DOUBLE, NPY_INT, NPY_DOUBLE,
};
// 定义一个结构体描述符,用于描述通用函数(ufunc)
typedef struct gufunc_descriptor_struct {
const char *name;
const char *signature;
const char *doc;
int ntypes;
int nin;
int nout;
PyUFuncGenericFunction *funcs;
const char *types;
} GUFUNC_DESCRIPTOR_t;
// 定义一个数组,包含通用函数描述符结构体,描述不同的通用函数
GUFUNC_DESCRIPTOR_t gufunc_descriptors [] = {
{
"slogdet",
"(m,m)->(),()",
"slogdet on the last two dimensions and broadcast on the rest. \n"\
"Results in two arrays, one with sign and the other with log of the"\
" determinants. \n"\
" \"(m,m)->(),()\" \n",
4, 1, 2,
FUNC_ARRAY_NAME(slogdet),
slogdet_types
},
{
"det",
"(m,m)->()",
"det of the last two dimensions and broadcast on the rest. \n"\
" \"(m,m)->()\" \n",
4, 1, 1,
FUNC_ARRAY_NAME(det),
equal_2_types
},
{
"eigh_lo",
"(m,m)->(m),(m,m)",
"eigh on the last two dimension and broadcast to the rest, using"\
" lower triangle \n"\
"Results in a vector of eigenvalues and a matrix with the"\
"eigenvectors. \n"\
" \"(m,m)->(m),(m,m)\" \n",
4, 1, 2,
FUNC_ARRAY_NAME(eighlo),
eigh_types
},
{
"eigh_up",
"(m,m)->(m),(m,m)",
"eigh on the last two dimension and broadcast to the rest, using"\
" upper triangle. \n"\
"Results in a vector of eigenvalues and a matrix with the"\
" eigenvectors. \n"\
" \"(m,m)->(m),(m,m)\" \n",
4, 1, 2,
FUNC_ARRAY_NAME(eighup),
eigh_types
},
{
"eigvalsh_lo",
"(m,m)->(m)",
"eigh on the last two dimension and broadcast to the rest, using"\
" lower triangle. \n"\
"Results in a vector of eigenvalues and a matrix with the"\
" eigenvectors. \n"\
" \"(m,m)->(m)\" \n",
4, 1, 1,
FUNC_ARRAY_NAME(eigvalshlo),
eighvals_types
},
{
"eigvalsh_up",
"(m,m)->(m)",
"eigvalsh on the last two dimension and broadcast to the rest,"\
" using upper triangle. \n"\
"Results in a vector of eigenvalues and a matrix with the"\
" eigenvectors.\n"\
" \"(m,m)->(m)\" \n",
4, 1, 1,
FUNC_ARRAY_NAME(eigvalshup),
eighvals_types
},
{
"solve",
"(m,m),(m,n)->(m,n)",
"solve the system a x = b, on the last two dimensions, broadcast"\
" to the rest. \n"\
"Results in matrices with the solutions. \n"\
" \"(m,m),(m,n)->(m,n)\" \n",
4, 2, 1,
FUNC_ARRAY_NAME(solve),
equal_3_types
},
{
"solve1",
"(m,m),(m)->(m)",
"solve the system a x = b, for b being a vector, broadcast in"\
" the outer dimensions. \n"\
"Results in vectors with the solutions. \n"\
" \"(m,m),(m)->(m)\" \n",
4, 2, 1,
FUNC_ARRAY_NAME(solve1),
equal_3_types
},
{
"inv",
"(m, m)->(m, m)",
"compute the inverse of the last two dimensions and broadcast"\
" to the rest. \n"\
"Results in the inverse matrices. \n"\
" \"(m,m)->(m,m)\" \n",
4, 1, 1,
FUNC_ARRAY_NAME(inv),
equal_2_types
},
{
"cholesky_lo",
"(m,m)->(m,m)",
"cholesky decomposition of hermitian positive-definite matrices,\n"\
"using lower triangle. Broadcast to all outer dimensions.\n"\
" \"(m,m)->(m,m)\"\n",
4, 1, 1,
FUNC_ARRAY_NAME(cholesky_lo),
equal_2_types
},
{
"cholesky_up",
"(m,m)->(m,m)",
"cholesky decomposition of hermitian positive-definite matrices,\n"\
"using upper triangle. Broadcast to all outer dimensions.\n"\
" \"(m,m)->(m,m)\"\n",
4, 1, 1,
FUNC_ARRAY_NAME(cholesky_up),
equal_2_types
},
{
"svd_m",
"(m,n)->(m)",
"svd when n>=m. ",
4, 1, 1,
FUNC_ARRAY_NAME(svd_N),
svd_1_1_types
},
{
"svd_n",
"(m,n)->(n)",
"svd when n<=m",
4, 1, 1,
FUNC_ARRAY_NAME(svd_N),
svd_1_1_types
},
{
"svd_m_s",
"(m,n)->(m,m),(m),(m,n)",
"svd when m<=n",
4, 1, 3,
FUNC_ARRAY_NAME(svd_S),
svd_1_3_types
},
{
"svd_n_s",
"(m,n)->(m,n),(n),(n,n)",
"svd when m>=n",
4, 1, 3,
FUNC_ARRAY_NAME(svd_S),
svd_1_3_types
},
{
"svd_m_f",
"(m,n)->(m,m),(m),(n,n)",
"svd when m<=n",
4, 1, 3,
FUNC_ARRAY_NAME(svd_A),
svd_1_3_types
},
{
"svd_n_f",
"(m,n)->(m,m),(n),(n,n)",
"svd when m>=n",
4, 1, 3,
FUNC_ARRAY_NAME(svd_A),
svd_1_3_types
},
{
"eig",
"(m,m)->(m),(m,m)",
"eig on the last two dimension and broadcast to the rest. \n"\
"Results in a vector with the eigenvalues and a matrix with the"\
" eigenvectors. \n"\
" \"(m,m)->(m),(m,m)\" \n",
3, 1, 2,
FUNC_ARRAY_NAME(eig),
eig_types
},
{
"eigvals",
"(m,m)->(m)",
"eigvals on the last two dimension and broadcast to the rest. \n"\
"Results in a vector of eigenvalues. \n",
3, 1, 1,
FUNC_ARRAY_NAME(eigvals),
eigvals_types
},
{
"qr_r_raw_m",
"(m,n)->(m)",
"Compute TAU vector for the last two dimensions \n"\
"and broadcast to the rest. For m <= n. \n",
2, 1, 1,
FUNC_ARRAY_NAME(qr_r_raw),
qr_r_raw_types
},
{
"qr_r_raw_n",
"(m,n)->(n)",
"Compute TAU vector for the last two dimensions \n"\
"and broadcast to the rest. For m > n. \n",
2, 1, 1,
FUNC_ARRAY_NAME(qr_r_raw),
qr_r_raw_types
},
{
"qr_reduced",
"(m,n),(k)->(m,k)",
"Compute Q matrix for the last two dimensions \n"\
"and broadcast to the rest. \n",
2, 2, 1,
FUNC_ARRAY_NAME(qr_reduced),
qr_reduced_types
},
{
"qr_complete",
"(m,n),(n)->(m,m)",
"Compute Q matrix for the last two dimensions \n"\
"and broadcast to the rest. For m > n. \n",
2, 2, 1,
FUNC_ARRAY_NAME(qr_complete),
qr_complete_types
},
{
"lstsq_m",
"(m,n),(m,nrhs),()->(n,nrhs),(nrhs),(),(m)",
"least squares on the last two dimensions and broadcast to the rest. \n"\
"For m <= n. \n",
4, 3, 4,
FUNC_ARRAY_NAME(lstsq),
lstsq_types
},
{
"lstsq_n",
"(m,n),(m,nrhs),()->(n,nrhs),(nrhs),(),(n)",
"least squares on the last two dimensions and broadcast to the rest. \n"\
"For m >= n, meaning that residuals are produced. \n",
4, 3, 4,
FUNC_ARRAY_NAME(lstsq),
lstsq_types
}
"svd_n": {
"description": "svd when n<=m",
"inputs": 4,
"outputs": 1,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(svd_N),
"types": svd_1_1_types
},
"svd_m_s": {
"description": "svd when m<=n",
"inputs": 4,
"outputs": 1,
"dimensionality": 3,
"function_name": FUNC_ARRAY_NAME(svd_S),
"types": svd_1_3_types
},
"svd_n_s": {
"description": "svd when m>=n",
"inputs": 4,
"outputs": 1,
"dimensionality": 3,
"function_name": FUNC_ARRAY_NAME(svd_S),
"types": svd_1_3_types
},
"svd_m_f": {
"description": "svd when m<=n",
"inputs": 4,
"outputs": 1,
"dimensionality": 3,
"function_name": FUNC_ARRAY_NAME(svd_A),
"types": svd_1_3_types
},
"svd_n_f": {
"description": "svd when m>=n",
"inputs": 4,
"outputs": 1,
"dimensionality": 3,
"function_name": FUNC_ARRAY_NAME(svd_A),
"types": svd_1_3_types
},
"eig": {
"description": "eig on the last two dimension and broadcast to the rest. Results in a vector with the eigenvalues and a matrix with the eigenvectors.",
"inputs": 3,
"outputs": 1,
"dimensionality": 2,
"function_name": FUNC_ARRAY_NAME(eig),
"types": eig_types
},
"eigvals": {
"description": "eigvals on the last two dimension and broadcast to the rest. Results in a vector of eigenvalues.",
"inputs": 3,
"outputs": 1,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(eigvals),
"types": eigvals_types
},
"qr_r_raw_m": {
"description": "Compute TAU vector for the last two dimensions and broadcast to the rest. For m <= n.",
"inputs": 2,
"outputs": 1,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(qr_r_raw),
"types": qr_r_raw_types
},
"qr_r_raw_n": {
"description": "Compute TAU vector for the last two dimensions and broadcast to the rest. For m > n.",
"inputs": 2,
"outputs": 1,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(qr_r_raw),
"types": qr_r_raw_types
},
"qr_reduced": {
"description": "Compute Q matrix for the last two dimensions and broadcast to the rest.",
"inputs": 2,
"outputs": 2,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(qr_reduced),
"types": qr_reduced_types
},
"qr_complete": {
"description": "Compute Q matrix for the last two dimensions and broadcast to the rest. For m > n.",
"inputs": 2,
"outputs": 2,
"dimensionality": 1,
"function_name": FUNC_ARRAY_NAME(qr_complete),
"types": qr_complete_types
},
"lstsq_m": {
"description": "least squares on the last two dimensions and broadcast to the rest. For m <= n.",
"inputs": 4,
"outputs": 3,
"dimensionality": 4,
"function_name": FUNC_ARRAY_NAME(lstsq),
"types": lstsq_types
},
"lstsq_n": {
"description": "least squares on the last two dimensions and broadcast to the rest. For m >= n, meaning that residuals are produced.",
"inputs": 4,
"outputs": 3,
"dimensionality": 4,
"function_name": FUNC_ARRAY_NAME(lstsq),
"types": lstsq_types
}
};
static int
addUfuncs(PyObject *dictionary) {
PyObject *f;
int i;
const int gufunc_count = sizeof(gufunc_descriptors)/
sizeof(gufunc_descriptors[0]);
// 遍历 gufunc_descriptors 数组,为每个描述符创建 PyUFuncObject 对象并添加到给定的字典中
for (i = 0; i < gufunc_count; i++) {
GUFUNC_DESCRIPTOR_t* d = &gufunc_descriptors[i];
// 使用描述符 d 创建 PyUFuncObject 对象 f
f = PyUFunc_FromFuncAndDataAndSignature(d->funcs,
array_of_nulls,
d->types,
d->ntypes,
d->nin,
d->nout,
PyUFunc_None,
d->name,
d->doc,
0,
d->signature);
if (f == NULL) {
return -1; // 如果创建失败,返回 -1 表示错误
}
// 在调试模式下,输出 PyUFuncObject 对象的信息
dump_ufunc_object((PyUFuncObject*) f);
// 将创建的 PyUFuncObject 对象 f 添加到字典中,使用描述符的名称作为键
int ret = PyDict_SetItemString(dictionary, d->name, f);
Py_DECREF(f);
if (ret < 0) {
return -1; // 如果添加失败,返回 -1 表示错误
}
}
return 0; // 成功添加所有的 ufunc 到字典中,返回 0 表示成功
}
/* -------------------------------------------------------------------------- */
/* Module initialization stuff */
static PyMethodDef UMath_LinAlgMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
UMATH_LINALG_MODULE_NAME,
NULL,
-1,
UMath_LinAlgMethods,
NULL,
NULL,
NULL,
NULL
};
// Python 模块初始化函数,创建并返回一个新的 PyModuleObject 对象
PyMODINIT_FUNC PyInit__umath_linalg(void)
{
PyObject *m;
PyObject *d;
PyObject *version;
// 创建一个新的 Python 模块对象
m = PyModule_Create(&moduledef);
if (m == NULL) {
return NULL; // 如果创建失败,返回 NULL 表示错误
}
// 导入 NumPy 的 C API,确保 NumPy 的数据结构可以在模块中使用
import_array();
// 导入 NumPy 的通用函数 API,确保通用函数可以在模块中使用
import_ufunc();
// 获取模块 m 的字典对象
d = PyModule_GetDict(m);
if (d == NULL) {
return NULL; // 如果获取失败,返回 NULL 表示错误
}
// 创建包含模块版本信息的字符串对象
version = PyUnicode_FromString(umath_linalg_version_string);
if (version == NULL) {
return NULL; // 如果创建失败,返回 NULL 表示错误
}
// 将版本信息添加到模块的字典中
int ret = PyDict_SetItemString(d, "__version__", version);
Py_DECREF(version);
if (ret < 0) {
return NULL; // 如果添加失败,返回 NULL 表示错误
}
/* Load the ufunc operators into the module's namespace */
// 将 ufunc 操作符加载到模块的命名空间中
if (addUfuncs(d) < 0) {
return NULL; // 如果加载失败,返回 NULL 表示错误
}
// 根据编译配置添加 _ilp64 到模块的字典中,表示是否支持 64 位整数 BLAS 操作
#ifdef HAVE_BLAS_ILP64
PyDict_SetItemString(d, "_ilp64", Py_True);
#else
PyDict_SetItemString(d, "_ilp64", Py_False);
#endif
// 返回创建的 Python 模块对象
return m;
}