numo-linalg-alt 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +3 -1
- data/ext/numo/linalg/lapack/gees.c +257 -0
- data/ext/numo/linalg/lapack/gees.h +15 -0
- data/ext/numo/linalg/linalg.c +1 -0
- data/ext/numo/linalg/linalg.h +1 -0
- data/lib/numo/linalg/version.rb +1 -1
- data/lib/numo/linalg.rb +186 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf52b8241dd646725159141067e9e38948c95d89d459f81e8ec5b19a20ce7a69
|
4
|
+
data.tar.gz: 286fb08653029ff6bfd745323fd7b8e1165a9d6027bbcf3871bb5333eae8eddd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7205197001dd99f404a39e63ac7caf6a88fa653ab9487756d3ef69b03c2c9abedc0c3900a1284ae70d41415ddc97f9180e1977d765e3b5f46578a72c67429cf
|
7
|
+
data.tar.gz: 3b18b323a4d0873f19c5514ca3856de7651078ef227559f2c55ae9f82f6cb05b4a2bf28958a4c499d7909741e6ad0e58ae53b5821681d6d0f9a363b3719291da
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
## [[0.
|
1
|
+
## [[0.3.0](https://github.com/yoshoku/numo-linalg-alt/compare/v0.2.0...v0.3.0)] - 2025-10-06
|
2
|
+
|
3
|
+
- add `schur`, `cosm`, `sinm`, `orthogonal_procrustes`, and `polar` module functions to Numo::Linalg.
|
4
|
+
- fix version specifier for numo-narray-alt.
|
5
|
+
|
6
|
+
## [[0.2.0](https://github.com/yoshoku/numo-linalg-alt/compare/d010476...ea50089)] - 2025-09-29
|
2
7
|
|
3
8
|
- fork from [Numo::TinyLinalg main branch](https://github.com/yoshoku/numo-tiny_linalg/tree/d0104765c560e9664a868b7a3e2f3144bd32c428)
|
4
9
|
- rewrite native extensions with C programming language.
|
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# Numo::Linalg Alternative
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/numo-linalg-alt)
|
3
4
|
[](https://github.com/yoshoku/numo-linalg-alt/actions/workflows/main.yml)
|
4
5
|
[](https://github.com/yoshoku/numo-linalg-alt/blob/main/LICENSE.txt)
|
6
|
+
[](https://gemdocs.org/gems/numo-linalg-alt/)
|
5
7
|
|
6
8
|
Numo::Linalg Alternative (numo-linalg-alt) is an alternative to [Numo::Linalg](https://github.com/ruby-numo/numo-linalg).
|
7
9
|
Unlike Numo::Linalg, numo-linalg-alt depends on [Numo::NArray Alterntive](https://github.com/yoshoku/numo-narray-alt).
|
@@ -99,7 +101,7 @@ This project is intended to be a safe, welcoming space for collaboration, and co
|
|
99
101
|
|
100
102
|
## Code of Conduct
|
101
103
|
|
102
|
-
Everyone interacting in the Numo::
|
104
|
+
Everyone interacting in the Numo::Linalg Alternative project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yoshoku/numo-linalg-alt/blob/main/CODE_OF_CONDUCT.md).
|
103
105
|
|
104
106
|
## License
|
105
107
|
|
@@ -0,0 +1,257 @@
|
|
1
|
+
#include "gees.h"
|
2
|
+
|
3
|
+
char _get_jobvs(VALUE val) {
|
4
|
+
const char jobvs = NUM2CHR(val);
|
5
|
+
if (jobvs != 'N' && jobvs != 'V') {
|
6
|
+
rb_raise(rb_eArgError, "jobvs must be 'N' or 'V'");
|
7
|
+
}
|
8
|
+
return jobvs;
|
9
|
+
}
|
10
|
+
|
11
|
+
#define DEF_GEES_OPTION(fLapackFunc, tSelectFunc) \
|
12
|
+
struct _gees_option_##fLapackFunc { \
|
13
|
+
int matrix_layout; \
|
14
|
+
char jobvs; \
|
15
|
+
char sort; \
|
16
|
+
tSelectFunc select; \
|
17
|
+
};
|
18
|
+
|
19
|
+
#define DEF_GEES_SORT_FUNC(tDType, fLapackFunc) \
|
20
|
+
lapack_logical _sort_nil_##fLapackFunc(const tDType* wr, const tDType* wi) { \
|
21
|
+
return 0; \
|
22
|
+
} \
|
23
|
+
lapack_logical _sort_lhp_##fLapackFunc(const tDType* wr, const tDType* wi) { \
|
24
|
+
if (*wr < (tDType)0) { \
|
25
|
+
return 1; \
|
26
|
+
} \
|
27
|
+
return 0; \
|
28
|
+
} \
|
29
|
+
lapack_logical _sort_rhp_##fLapackFunc(const tDType* wr, const tDType* wi) { \
|
30
|
+
if (*wr >= (tDType)0) { \
|
31
|
+
return 1; \
|
32
|
+
} \
|
33
|
+
return 0; \
|
34
|
+
} \
|
35
|
+
lapack_logical _sort_iup_##fLapackFunc(const tDType* wr, const tDType* wi) { \
|
36
|
+
tDType magnitude = *wr * *wr + *wi * *wi; \
|
37
|
+
if (magnitude <= (tDType)1) { \
|
38
|
+
return 1; \
|
39
|
+
} \
|
40
|
+
return 0; \
|
41
|
+
} \
|
42
|
+
lapack_logical _sort_ouc_##fLapackFunc(const tDType* wr, const tDType* wi) { \
|
43
|
+
tDType magnitude = *wr * *wr + *wi * *wi; \
|
44
|
+
if (magnitude > (tDType)1) { \
|
45
|
+
return 1; \
|
46
|
+
} \
|
47
|
+
return 0; \
|
48
|
+
}
|
49
|
+
|
50
|
+
#define DEF_GEES_SORT_FUNC_COMPLEX(tDType, tElType, fLapackRealFunc, fLapackImagFunc, fLapackFunc) \
|
51
|
+
lapack_logical _sort_nil_##fLapackFunc(const tDType* w) { \
|
52
|
+
return 0; \
|
53
|
+
} \
|
54
|
+
lapack_logical _sort_lhp_##fLapackFunc(const tDType* w) { \
|
55
|
+
if (fLapackRealFunc(*w) < 0.0) { \
|
56
|
+
return 1; \
|
57
|
+
} \
|
58
|
+
return 0; \
|
59
|
+
} \
|
60
|
+
lapack_logical _sort_rhp_##fLapackFunc(const tDType* w) { \
|
61
|
+
if (fLapackRealFunc(*w) >= 0.0) { \
|
62
|
+
return 1; \
|
63
|
+
} \
|
64
|
+
return 0; \
|
65
|
+
} \
|
66
|
+
lapack_logical _sort_iup_##fLapackFunc(const tDType* w) { \
|
67
|
+
tElType real = fLapackRealFunc(*w); \
|
68
|
+
tElType imag = fLapackImagFunc(*w); \
|
69
|
+
tElType magnitude = real * real + imag * imag; \
|
70
|
+
if (magnitude <= (tElType)1.0) { \
|
71
|
+
return 1; \
|
72
|
+
} \
|
73
|
+
return 0; \
|
74
|
+
} \
|
75
|
+
lapack_logical _sort_ouc_##fLapackFunc(const tDType* w) { \
|
76
|
+
tElType real = fLapackRealFunc(*w); \
|
77
|
+
tElType imag = fLapackImagFunc(*w); \
|
78
|
+
tElType magnitude = real * real + imag * imag; \
|
79
|
+
if (magnitude > (tElType)1.0) { \
|
80
|
+
return 1; \
|
81
|
+
} \
|
82
|
+
return 0; \
|
83
|
+
}
|
84
|
+
|
85
|
+
#define DEF_LINALG_FUNC(tDType, tNAryClass, fLapackFunc) \
|
86
|
+
static void _iter_##fLapackFunc(na_loop_t* const lp) { \
|
87
|
+
tDType* a = (tDType*)(NDL_PTR(lp, 0)); \
|
88
|
+
tDType* wr = (tDType*)(NDL_PTR(lp, 1)); \
|
89
|
+
tDType* wi = (tDType*)(NDL_PTR(lp, 2)); \
|
90
|
+
tDType* vs = (tDType*)(NDL_PTR(lp, 3)); \
|
91
|
+
int* sdim = (int*)(NDL_PTR(lp, 4)); \
|
92
|
+
int* info = (int*)(NDL_PTR(lp, 5)); \
|
93
|
+
struct _gees_option_##fLapackFunc* opt = (struct _gees_option_##fLapackFunc*)(lp->opt_ptr); \
|
94
|
+
const lapack_int n = (lapack_int)(opt->matrix_layout == LAPACK_ROW_MAJOR ? NDL_SHAPE(lp, 0)[0] : NDL_SHAPE(lp, 0)[1]); \
|
95
|
+
const lapack_int lda = n; \
|
96
|
+
const lapack_int ldvs = (opt->jobvs == 'N') ? 1 : n; \
|
97
|
+
lapack_int s = 0; \
|
98
|
+
lapack_int i = LAPACKE_##fLapackFunc(opt->matrix_layout, opt->jobvs, opt->sort, opt->select, n, a, lda, &s, wr, wi, vs, ldvs); \
|
99
|
+
*sdim = (int)s; \
|
100
|
+
*info = (int)i; \
|
101
|
+
} \
|
102
|
+
\
|
103
|
+
static VALUE _linalg_lapack_##fLapackFunc(int argc, VALUE* argv, VALUE self) { \
|
104
|
+
VALUE a_vnary = Qnil; \
|
105
|
+
VALUE kw_args = Qnil; \
|
106
|
+
rb_scan_args(argc, argv, "1:", &a_vnary, &kw_args); \
|
107
|
+
ID kw_table[3] = { rb_intern("order"), rb_intern("jobvs"), rb_intern("sort") }; \
|
108
|
+
VALUE kw_values[3] = { Qundef, Qundef, Qundef }; \
|
109
|
+
rb_get_kwargs(kw_args, kw_table, 0, 3, kw_values); \
|
110
|
+
const int matrix_layout = kw_values[0] != Qundef ? get_matrix_layout(kw_values[0]) : LAPACK_ROW_MAJOR; \
|
111
|
+
const char jobvs = kw_values[1] != Qundef ? _get_jobvs(kw_values[1]) : 'V'; \
|
112
|
+
VALUE sort_val = kw_values[2] != Qundef ? kw_values[2] : Qnil; \
|
113
|
+
const char sort_ch = NIL_P(sort_val) ? 'N' : 'S'; \
|
114
|
+
\
|
115
|
+
if (CLASS_OF(a_vnary) != tNAryClass) { \
|
116
|
+
a_vnary = rb_funcall(tNAryClass, rb_intern("cast"), 1, a_vnary); \
|
117
|
+
} \
|
118
|
+
if (!RTEST(nary_check_contiguous(a_vnary))) { \
|
119
|
+
a_vnary = nary_dup(a_vnary); \
|
120
|
+
} \
|
121
|
+
\
|
122
|
+
narray_t* a_nary = NULL; \
|
123
|
+
GetNArray(a_vnary, a_nary); \
|
124
|
+
if (NA_NDIM(a_nary) != 2) { \
|
125
|
+
rb_raise(rb_eArgError, "input array must be 2-dimensional array"); \
|
126
|
+
return Qnil; \
|
127
|
+
} \
|
128
|
+
\
|
129
|
+
size_t n = matrix_layout == LAPACK_ROW_MAJOR ? NA_SHAPE(a_nary)[0] : NA_SHAPE(a_nary)[1]; \
|
130
|
+
size_t shape_wr[1] = { n }; \
|
131
|
+
size_t shape_wi[1] = { n }; \
|
132
|
+
size_t shape_vs[2] = { n, jobvs == 'N' ? 1 : n }; \
|
133
|
+
ndfunc_arg_in_t ain[1] = { { OVERWRITE, 2 } }; \
|
134
|
+
ndfunc_arg_out_t aout[5] = { { tNAryClass, 1, shape_wr }, { tNAryClass, 1, shape_wi }, { tNAryClass, 2, shape_vs }, { numo_cInt32, 0 }, { numo_cInt32, 0 } }; \
|
135
|
+
ndfunc_t ndf = { _iter_##fLapackFunc, NO_LOOP | NDF_EXTRACT, 1, 5, ain, aout }; \
|
136
|
+
struct _gees_option_##fLapackFunc opt = { matrix_layout, jobvs, sort_ch, NULL }; \
|
137
|
+
const char* sort_str = NIL_P(sort_val) ? "" : StringValueCStr(sort_val); \
|
138
|
+
if (NIL_P(sort_val)) { \
|
139
|
+
opt.select = _sort_nil_##fLapackFunc; \
|
140
|
+
} else if (strcmp(sort_str, "lhp") == 0) { \
|
141
|
+
opt.select = _sort_lhp_##fLapackFunc; \
|
142
|
+
} else if (strcmp(sort_str, "rhp") == 0) { \
|
143
|
+
opt.select = _sort_rhp_##fLapackFunc; \
|
144
|
+
} else if (strcmp(sort_str, "iup") == 0) { \
|
145
|
+
opt.select = _sort_iup_##fLapackFunc; \
|
146
|
+
} else if (strcmp(sort_str, "ouc") == 0) { \
|
147
|
+
opt.select = _sort_ouc_##fLapackFunc; \
|
148
|
+
} else { \
|
149
|
+
rb_raise(rb_eArgError, "invalid value for sort option"); \
|
150
|
+
return Qnil; \
|
151
|
+
} \
|
152
|
+
VALUE ret = na_ndloop3(&ndf, &opt, 1, a_vnary); \
|
153
|
+
\
|
154
|
+
RB_GC_GUARD(sort_val); \
|
155
|
+
RB_GC_GUARD(a_vnary); \
|
156
|
+
return ret; \
|
157
|
+
}
|
158
|
+
|
159
|
+
#define DEF_LINALG_FUNC_COMPLEX(tDType, tNAryClass, fLapackFunc) \
|
160
|
+
static void _iter_##fLapackFunc(na_loop_t* const lp) { \
|
161
|
+
tDType* a = (tDType*)(NDL_PTR(lp, 0)); \
|
162
|
+
tDType* w = (tDType*)(NDL_PTR(lp, 1)); \
|
163
|
+
tDType* vs = (tDType*)(NDL_PTR(lp, 2)); \
|
164
|
+
int* sdim = (int*)(NDL_PTR(lp, 3)); \
|
165
|
+
int* info = (int*)(NDL_PTR(lp, 4)); \
|
166
|
+
struct _gees_option_##fLapackFunc* opt = (struct _gees_option_##fLapackFunc*)(lp->opt_ptr); \
|
167
|
+
const lapack_int n = (lapack_int)(opt->matrix_layout == LAPACK_ROW_MAJOR ? NDL_SHAPE(lp, 0)[0] : NDL_SHAPE(lp, 0)[1]); \
|
168
|
+
const lapack_int lda = n; \
|
169
|
+
const lapack_int ldvs = (opt->jobvs == 'N') ? 1 : n; \
|
170
|
+
lapack_int s = 0; \
|
171
|
+
lapack_int i = LAPACKE_##fLapackFunc(opt->matrix_layout, opt->jobvs, opt->sort, opt->select, n, a, lda, &s, w, vs, ldvs); \
|
172
|
+
*sdim = (int)s; \
|
173
|
+
*info = (int)i; \
|
174
|
+
} \
|
175
|
+
\
|
176
|
+
static VALUE _linalg_lapack_##fLapackFunc(int argc, VALUE* argv, VALUE self) { \
|
177
|
+
VALUE a_vnary = Qnil; \
|
178
|
+
VALUE kw_args = Qnil; \
|
179
|
+
rb_scan_args(argc, argv, "1:", &a_vnary, &kw_args); \
|
180
|
+
ID kw_table[3] = { rb_intern("order"), rb_intern("jobvs"), rb_intern("sort") }; \
|
181
|
+
VALUE kw_values[3] = { Qundef, Qundef, Qundef }; \
|
182
|
+
rb_get_kwargs(kw_args, kw_table, 0, 3, kw_values); \
|
183
|
+
const int matrix_layout = kw_values[0] != Qundef ? get_matrix_layout(kw_values[0]) : LAPACK_ROW_MAJOR; \
|
184
|
+
const char jobvs = kw_values[1] != Qundef ? _get_jobvs(kw_values[1]) : 'V'; \
|
185
|
+
VALUE sort_val = kw_values[2] != Qundef ? kw_values[2] : Qnil; \
|
186
|
+
const char sort_ch = NIL_P(sort_val) ? 'N' : 'S'; \
|
187
|
+
\
|
188
|
+
if (CLASS_OF(a_vnary) != tNAryClass) { \
|
189
|
+
a_vnary = rb_funcall(tNAryClass, rb_intern("cast"), 1, a_vnary); \
|
190
|
+
} \
|
191
|
+
if (!RTEST(nary_check_contiguous(a_vnary))) { \
|
192
|
+
a_vnary = nary_dup(a_vnary); \
|
193
|
+
} \
|
194
|
+
\
|
195
|
+
narray_t* a_nary = NULL; \
|
196
|
+
GetNArray(a_vnary, a_nary); \
|
197
|
+
if (NA_NDIM(a_nary) != 2) { \
|
198
|
+
rb_raise(rb_eArgError, "input array must be 2-dimensional array"); \
|
199
|
+
return Qnil; \
|
200
|
+
} \
|
201
|
+
\
|
202
|
+
size_t n = matrix_layout == LAPACK_ROW_MAJOR ? NA_SHAPE(a_nary)[0] : NA_SHAPE(a_nary)[1]; \
|
203
|
+
size_t shape_w[1] = { n }; \
|
204
|
+
size_t shape_vs[2] = { n, jobvs == 'N' ? 1 : n }; \
|
205
|
+
ndfunc_arg_in_t ain[1] = { { OVERWRITE, 2 } }; \
|
206
|
+
ndfunc_arg_out_t aout[4] = { { tNAryClass, 1, shape_w }, { tNAryClass, 2, shape_vs }, { numo_cInt32, 0 }, { numo_cInt32, 0 } }; \
|
207
|
+
ndfunc_t ndf = { _iter_##fLapackFunc, NO_LOOP | NDF_EXTRACT, 1, 4, ain, aout }; \
|
208
|
+
struct _gees_option_##fLapackFunc opt = { matrix_layout, jobvs, sort_ch, NULL }; \
|
209
|
+
const char* sort_str = NIL_P(sort_val) ? "" : StringValueCStr(sort_val); \
|
210
|
+
if (NIL_P(sort_val)) { \
|
211
|
+
opt.select = _sort_nil_##fLapackFunc; \
|
212
|
+
} else if (strcmp(sort_str, "lhp") == 0) { \
|
213
|
+
opt.select = _sort_lhp_##fLapackFunc; \
|
214
|
+
} else if (strcmp(sort_str, "rhp") == 0) { \
|
215
|
+
opt.select = _sort_rhp_##fLapackFunc; \
|
216
|
+
} else if (strcmp(sort_str, "iup") == 0) { \
|
217
|
+
opt.select = _sort_iup_##fLapackFunc; \
|
218
|
+
} else if (strcmp(sort_str, "ouc") == 0) { \
|
219
|
+
opt.select = _sort_ouc_##fLapackFunc; \
|
220
|
+
} else { \
|
221
|
+
rb_raise(rb_eArgError, "invalid value for sort option"); \
|
222
|
+
return Qnil; \
|
223
|
+
} \
|
224
|
+
VALUE ret = na_ndloop3(&ndf, &opt, 1, a_vnary); \
|
225
|
+
\
|
226
|
+
RB_GC_GUARD(sort_val); \
|
227
|
+
RB_GC_GUARD(a_vnary); \
|
228
|
+
return ret; \
|
229
|
+
}
|
230
|
+
|
231
|
+
DEF_GEES_OPTION(dgees, LAPACK_D_SELECT2)
|
232
|
+
DEF_GEES_OPTION(sgees, LAPACK_S_SELECT2)
|
233
|
+
DEF_GEES_OPTION(zgees, LAPACK_Z_SELECT1)
|
234
|
+
DEF_GEES_OPTION(cgees, LAPACK_C_SELECT1)
|
235
|
+
|
236
|
+
DEF_GEES_SORT_FUNC(double, dgees)
|
237
|
+
DEF_GEES_SORT_FUNC(float, sgees)
|
238
|
+
DEF_GEES_SORT_FUNC_COMPLEX(lapack_complex_double, double, lapack_complex_double_real, lapack_complex_double_imag, zgees)
|
239
|
+
DEF_GEES_SORT_FUNC_COMPLEX(lapack_complex_float, float, lapack_complex_float_real, lapack_complex_float_imag, cgees)
|
240
|
+
|
241
|
+
DEF_LINALG_FUNC(double, numo_cDFloat, dgees)
|
242
|
+
DEF_LINALG_FUNC(float, numo_cSFloat, sgees)
|
243
|
+
DEF_LINALG_FUNC_COMPLEX(lapack_complex_double, numo_cDComplex, zgees)
|
244
|
+
DEF_LINALG_FUNC_COMPLEX(lapack_complex_float, numo_cSComplex, cgees)
|
245
|
+
|
246
|
+
#undef DEF_GEES_OPTION
|
247
|
+
#undef DEF_GEES_SORT_FUNC
|
248
|
+
#undef DEF_GEES_SORT_FUNC_COMPLEX
|
249
|
+
#undef DEF_LINALG_FUNC
|
250
|
+
#undef DEF_LINALG_FUNC_COMPLEX
|
251
|
+
|
252
|
+
void define_linalg_lapack_gees(VALUE mLapack) {
|
253
|
+
rb_define_module_function(mLapack, "dgees", RUBY_METHOD_FUNC(_linalg_lapack_dgees), -1);
|
254
|
+
rb_define_module_function(mLapack, "sgees", RUBY_METHOD_FUNC(_linalg_lapack_sgees), -1);
|
255
|
+
rb_define_module_function(mLapack, "zgees", RUBY_METHOD_FUNC(_linalg_lapack_zgees), -1);
|
256
|
+
rb_define_module_function(mLapack, "cgees", RUBY_METHOD_FUNC(_linalg_lapack_cgees), -1);
|
257
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#ifndef NUMO_LINALG_ALT_LAPACK_GEES_H
|
2
|
+
#define NUMO_LINALG_ALT_LAPACK_GEES_H 1
|
3
|
+
|
4
|
+
#include <lapacke.h>
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
|
8
|
+
#include <numo/narray.h>
|
9
|
+
#include <numo/template.h>
|
10
|
+
|
11
|
+
#include "../util.h"
|
12
|
+
|
13
|
+
void define_linalg_lapack_gees(VALUE mLapack);
|
14
|
+
|
15
|
+
#endif /* NUMO_LINALG_ALT_LAPACK_GEES_H */
|
data/ext/numo/linalg/linalg.c
CHANGED
@@ -257,6 +257,7 @@ void Init_linalg(void) {
|
|
257
257
|
define_linalg_lapack_geqrf(rb_mLinalgLapack);
|
258
258
|
define_linalg_lapack_orgqr(rb_mLinalgLapack);
|
259
259
|
define_linalg_lapack_ungqr(rb_mLinalgLapack);
|
260
|
+
define_linalg_lapack_gees(rb_mLinalgLapack);
|
260
261
|
define_linalg_lapack_geev(rb_mLinalgLapack);
|
261
262
|
define_linalg_lapack_gesv(rb_mLinalgLapack);
|
262
263
|
define_linalg_lapack_gesvd(rb_mLinalgLapack);
|
data/ext/numo/linalg/linalg.h
CHANGED
data/lib/numo/linalg/version.rb
CHANGED
data/lib/numo/linalg.rb
CHANGED
@@ -444,6 +444,54 @@ module Numo
|
|
444
444
|
u.dot(vh[0...rank, true]).conj.transpose
|
445
445
|
end
|
446
446
|
|
447
|
+
# Computes the polar decomposition of a matrix.
|
448
|
+
#
|
449
|
+
# https://en.wikipedia.org/wiki/Polar_decomposition
|
450
|
+
#
|
451
|
+
# @example
|
452
|
+
# require 'numo/linalg'
|
453
|
+
#
|
454
|
+
# a = Numo::DFloat[[0.5, 1, 2], [1.5, 3, 4]]
|
455
|
+
# u, p = Numo::Linalg.polar(a)
|
456
|
+
# pp u.dot(p)
|
457
|
+
# # =>
|
458
|
+
# # Numo::DFloat#shape=[2,3]
|
459
|
+
# # [[0.5, 1, 2],
|
460
|
+
# # [1.5, 3, 4]]
|
461
|
+
# pp u.dot(u.transpose)
|
462
|
+
# # =>
|
463
|
+
# # Numo::DFloat#shape=[2,2]
|
464
|
+
# # [[1, -1.68043e-16],
|
465
|
+
# # [-1.68043e-16, 1]]
|
466
|
+
#
|
467
|
+
# u, p = Numo::Linalg.polar(a, side: 'left')
|
468
|
+
# pp p.dot(u)
|
469
|
+
# # =>
|
470
|
+
# # Numo::DFloat#shape=[2,3]
|
471
|
+
# # [[0.5, 1, 2],
|
472
|
+
# # [1.5, 3, 4]]
|
473
|
+
#
|
474
|
+
# @param a [Numo::NArray] The m-by-n matrix to be decomposed.
|
475
|
+
# @param side [String] The side of polar decomposition ('right' or 'left').
|
476
|
+
# @return [Array<Numo::NArray>] The unitary matrix `U` and the positive-semidefinite Hermitian matrix `P`
|
477
|
+
# such that `A = U * P` if side='right', or `A = P * U` if side='left'.
|
478
|
+
def polar(a, side: 'right')
|
479
|
+
raise Numo::NArray::ShapeError, 'input array a must be 2-dimensional' if a.ndim != 2
|
480
|
+
raise ArugumentError, "invalid side: #{side}" unless %w[left right].include?(side)
|
481
|
+
|
482
|
+
bchr = blas_char(a)
|
483
|
+
raise ArgumentError, "invalid array type: #{a.class}" if bchr == 'n'
|
484
|
+
|
485
|
+
s, w, vh = svd(a, driver: 'svd', job: 'S')
|
486
|
+
u = w.dot(vh)
|
487
|
+
p_mat = if side == 'right'
|
488
|
+
vh.transpose.conj.dot(s.diag).dot(vh)
|
489
|
+
else
|
490
|
+
w.dot(s.diag).dot(w.transpose.conj)
|
491
|
+
end
|
492
|
+
[u, p_mat]
|
493
|
+
end
|
494
|
+
|
447
495
|
# Computes the QR decomposition of a matrix.
|
448
496
|
#
|
449
497
|
# @example
|
@@ -512,6 +560,66 @@ module Numo
|
|
512
560
|
[q, r]
|
513
561
|
end
|
514
562
|
|
563
|
+
# Computes the Schur decomposition of a square matrix.
|
564
|
+
# The Schur decomposition is given by `A = Z * T * Z^H`,
|
565
|
+
# where `A` is the input matrix, `Z` is a unitary matrix,
|
566
|
+
# and `T` is an upper triangular matrix (or quasi-upper triangular matrix in real case).
|
567
|
+
#
|
568
|
+
# @example
|
569
|
+
# require 'numo/linalg'
|
570
|
+
#
|
571
|
+
# a = Numo::DFloat[[0, 2, 3], [4, 5, 6], [7, 8, 9]]
|
572
|
+
# t, z, sdim = Numo::Linalg.schur(a)
|
573
|
+
# pp t
|
574
|
+
# # =>
|
575
|
+
# # Numo::DFloat#shape=[3,3]
|
576
|
+
# # [[16.0104, 4.81155, 0.920982],
|
577
|
+
# # [0, -1.91242, 0.0274406],
|
578
|
+
# # [0, 0, -0.0979794]]
|
579
|
+
# pp z
|
580
|
+
# # =>
|
581
|
+
# # Numo::DFloat#shape=[3,3]
|
582
|
+
# # [[-0.219668, -0.94667, 0.235716],
|
583
|
+
# # [-0.527141, -0.0881306, -0.845195],
|
584
|
+
# # [-0.820895, 0.309918, 0.479669]]
|
585
|
+
# pp sdim
|
586
|
+
# # => 0
|
587
|
+
# pp (a - z.dot(t).dot(z.transpose)).abs.max
|
588
|
+
# # => 1.0658141036401503e-14
|
589
|
+
#
|
590
|
+
# @param a [Numo::NArray] The n-by-n square matrix.
|
591
|
+
# @param sort [String/Nil] The option for sorting eigenvalues ('lhp', 'rhp', 'iuc', 'ouc', or nil).
|
592
|
+
# - 'lhp': eigenvalue.real < 0
|
593
|
+
# - 'rhp': eigenvalue.real >= 0
|
594
|
+
# - 'iuc': eigenvalue.abs <= 1
|
595
|
+
# - 'ouc': eigenvalue.abs > 1
|
596
|
+
# @return [Array<Numo::NArray, Numo::NArray, Integer>] The Schur form `T`, the unitary matrix `Z`,
|
597
|
+
# and the number of eigenvalues for which the sorting condition is true.
|
598
|
+
def schur(a, sort: nil)
|
599
|
+
raise Numo::NArray::ShapeError, 'input array a must be 2-dimensional' if a.ndim != 2
|
600
|
+
raise Numo::NArray::ShapeError, 'input array a must be square' if a.shape[0] != a.shape[1]
|
601
|
+
raise ArgumentError, "invalid sort: #{sort}" unless sort.nil? || %w[lhp rhp iuc ouc].include?(sort)
|
602
|
+
|
603
|
+
bchr = blas_char(a)
|
604
|
+
raise ArgumentError, "invalid array type: #{a.class}" if bchr == 'n'
|
605
|
+
|
606
|
+
fnc = :"#{bchr}gees"
|
607
|
+
b = a.dup
|
608
|
+
if %w[d s].include?(bchr)
|
609
|
+
_wr, _wi, v, sdim, info = Numo::Linalg::Lapack.send(fnc, b, jobvs: 'V', sort: sort)
|
610
|
+
else
|
611
|
+
_w, v, sdim, info = Numo::Linalg::Lapack.send(fnc, b, jobvs: 'V', sort: sort)
|
612
|
+
end
|
613
|
+
|
614
|
+
n = a.shape[0]
|
615
|
+
raise "the #{-info}-th argument of #{fnc} had illegal value" if info.negative?
|
616
|
+
raise 'the QR algorithm failed to compute all the eigenvalues.' if info.positive? && info <= n
|
617
|
+
raise 'the eigenvalues could not be reordered.' if info == n + 1
|
618
|
+
raise 'after reordering, roundoff changed values of some complex eigenvalues.' if info == n + 2
|
619
|
+
|
620
|
+
[b, v, sdim]
|
621
|
+
end
|
622
|
+
|
515
623
|
# Solves linear equation `A * x = b` or `A * X = B` for `x` from square matrix `A`.
|
516
624
|
#
|
517
625
|
# @example
|
@@ -921,6 +1029,46 @@ module Numo
|
|
921
1029
|
c
|
922
1030
|
end
|
923
1031
|
|
1032
|
+
# Computes the orthogonal Procrustes problem.
|
1033
|
+
#
|
1034
|
+
# https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem
|
1035
|
+
#
|
1036
|
+
# @example
|
1037
|
+
# require 'numo/linalg'
|
1038
|
+
#
|
1039
|
+
# a = Numo::DFloat[[2, 0, 1], [-2, 0, 0]]
|
1040
|
+
# b = a.fliplr
|
1041
|
+
# r, scale = Numo::Linalg.orthogonal_procrustes(a, b)
|
1042
|
+
#
|
1043
|
+
# pp b
|
1044
|
+
# # =>
|
1045
|
+
# # Numo::DFloat(view)#shape=[2,3]
|
1046
|
+
# # [[1, 0, 2],
|
1047
|
+
# # [0, 0, -2]]
|
1048
|
+
# pp a.dot(r)
|
1049
|
+
# # =>
|
1050
|
+
# # Numo::DFloat#shape=[2,3]
|
1051
|
+
# # [[1, 0, 2],
|
1052
|
+
# # [1.58669e-16, 0, -2]]
|
1053
|
+
# pp (b - a.dot(r)).abs.max
|
1054
|
+
# # =>
|
1055
|
+
# # 2.220446049250313e-16
|
1056
|
+
#
|
1057
|
+
# @param a [Numo::NArray] The first input matrix.
|
1058
|
+
# @param b [Numo::NArray] The second input matrix.
|
1059
|
+
# @return [Array<Numo::NArray, Float>] The orthogonal matrix `R` and the scale factor `scale`.
|
1060
|
+
def orthogonal_procrustes(a, b)
|
1061
|
+
raise Numo::NArray::ShapeError, 'input array a must be 2-dimensional' if a.ndim != 2
|
1062
|
+
raise Numo::NArray::ShapeError, 'input array b must be 2-dimensional' if b.ndim != 2
|
1063
|
+
raise Numo::NArray::ShapeError, "incompatible dimensions: a.shape = #{a.shape} != b.shape = #{b.shape}" if a.shape != b.shape
|
1064
|
+
|
1065
|
+
m = b.transpose.dot(a.conj).transpose
|
1066
|
+
s, u, vt = svd(m, driver: 'svd', job: 'S')
|
1067
|
+
r = u.dot(vt)
|
1068
|
+
scale = s.sum
|
1069
|
+
[r, scale]
|
1070
|
+
end
|
1071
|
+
|
924
1072
|
# Computes the eigenvalues and right and/or left eigenvectors of a general square matrix.
|
925
1073
|
#
|
926
1074
|
# @example
|
@@ -1195,6 +1343,44 @@ module Numo
|
|
1195
1343
|
a_expm
|
1196
1344
|
end
|
1197
1345
|
|
1346
|
+
# Computes the matrix sine using the matrix exponential.
|
1347
|
+
#
|
1348
|
+
# @param a [Numo::NArray] The n-by-n square matrix.
|
1349
|
+
# @return [Numo::NArray] The matrix sine of `a`.
|
1350
|
+
def sinm(a)
|
1351
|
+
raise Numo::NArray::ShapeError, 'input array a must be 2-dimensional' if a.ndim != 2
|
1352
|
+
raise Numo::NArray::ShapeError, 'input array a must be square' if a.shape[0] != a.shape[1]
|
1353
|
+
|
1354
|
+
bchr = blas_char(a)
|
1355
|
+
raise ArgumentError, "invalid array type: #{a.class}" if bchr == 'n'
|
1356
|
+
|
1357
|
+
b = a * 1.0i
|
1358
|
+
if %w[z c].include?(bchr)
|
1359
|
+
-0.5i * (expm(b) - expm(-b))
|
1360
|
+
else
|
1361
|
+
expm(b).imag
|
1362
|
+
end
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
# Computes the matrix cosine using the matrix exponential.
|
1366
|
+
#
|
1367
|
+
# @param a [Numo::NArray] The n-by-n square matrix.
|
1368
|
+
# @return [Numo::NArray] The matrix cosine of `a`.
|
1369
|
+
def cosm(a)
|
1370
|
+
raise Numo::NArray::ShapeError, 'input array a must be 2-dimensional' if a.ndim != 2
|
1371
|
+
raise Numo::NArray::ShapeError, 'input array a must be square' if a.shape[0] != a.shape[1]
|
1372
|
+
|
1373
|
+
bchr = blas_char(a)
|
1374
|
+
raise ArgumentError, "invalid array type: #{a.class}" if bchr == 'n'
|
1375
|
+
|
1376
|
+
b = a * 1.0i
|
1377
|
+
if %w[z c].include?(bchr)
|
1378
|
+
0.5 * (expm(b) + expm(-b))
|
1379
|
+
else
|
1380
|
+
expm(b).real
|
1381
|
+
end
|
1382
|
+
end
|
1383
|
+
|
1198
1384
|
# Computes the inverse of a matrix using its LU decomposition.
|
1199
1385
|
#
|
1200
1386
|
# @param lu [Numo::NArray] The LU decomposition of the n-by-n matrix `A`.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numo-linalg-alt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yoshoku
|
@@ -13,14 +13,14 @@ dependencies:
|
|
13
13
|
name: numo-narray-alt
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
|
-
- - "
|
16
|
+
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
18
|
version: 0.9.3
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- - "
|
23
|
+
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: 0.9.3
|
26
26
|
description: |
|
@@ -50,6 +50,8 @@ files:
|
|
50
50
|
- ext/numo/linalg/converter.c
|
51
51
|
- ext/numo/linalg/converter.h
|
52
52
|
- ext/numo/linalg/extconf.rb
|
53
|
+
- ext/numo/linalg/lapack/gees.c
|
54
|
+
- ext/numo/linalg/lapack/gees.h
|
53
55
|
- ext/numo/linalg/lapack/geev.c
|
54
56
|
- ext/numo/linalg/lapack/geev.h
|
55
57
|
- ext/numo/linalg/lapack/gelsd.c
|
@@ -124,7 +126,7 @@ metadata:
|
|
124
126
|
homepage_uri: https://github.com/yoshoku/numo-linalg-alt
|
125
127
|
source_code_uri: https://github.com/yoshoku/numo-linalg-alt
|
126
128
|
changelog_uri: https://github.com/yoshoku/numo-linalg-alt/blob/main/CHANGELOG.md
|
127
|
-
documentation_uri: https://gemdocs.org/gems/numo-linalg-alt/0.
|
129
|
+
documentation_uri: https://gemdocs.org/gems/numo-linalg-alt/0.3.0/
|
128
130
|
rubygems_mfa_required: 'true'
|
129
131
|
rdoc_options: []
|
130
132
|
require_paths:
|