LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
tensor_nd_products.c
浏览该文件的文档.
1
6
7typedef struct {
8 size_t naxes;
10 const size_t* axes_a;
11 const size_t* axes_b;
14 size_t n_a_free;
15 size_t n_b_free;
17
18static int lmmc_tensor_nd_contraction_inputs_are_valid(
19 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
20 const lmmc_tensor_nd_t* out) {
21 if (!lmmc_tensor_nd_descriptor_is_valid(a)) {
22 return 0;
23 }
24 if (!lmmc_tensor_nd_descriptor_is_valid(b)) {
25 return 0;
26 }
27 return out != NULL;
28}
29
30static int lmmc_tensor_nd_contraction_alias_is_valid(
31 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
32 const lmmc_tensor_nd_t* out) {
33 if (out == a && a->owns_data) {
34 return 0;
35 }
36 if (out == b && b->owns_data) {
37 return 0;
38 }
39 return 1;
40}
41
42static int lmmc_tensor_nd_contraction_axes_are_valid(
43 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
44 const lmmc_tensor_nd_contraction_t* contraction) {
45 if (contraction->naxes > a->ndim) {
46 return 0;
47 }
48 if (contraction->naxes > b->ndim) {
49 return 0;
50 }
51 if (contraction->naxes == 0) {
52 return 1;
53 }
54 if (contraction->axes_a == NULL) {
55 return 0;
56 }
57 return contraction->axes_b != NULL;
58}
59
60static lmmc_status_t lmmc_tensor_nd_validate_contraction(
61 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
62 const lmmc_tensor_nd_contraction_t* contraction,
63 const lmmc_tensor_nd_t* out) {
64 if (!lmmc_tensor_nd_contraction_inputs_are_valid(a, b, out)) {
66 }
67 if (!lmmc_tensor_nd_contraction_alias_is_valid(a, b, out)) {
69 }
70 if (!lmmc_tensor_nd_contraction_axes_are_valid(a, b, contraction)) {
72 }
73 return LMMC_STATUS_OK;
74}
75
76static size_t lmmc_tensor_nd_free_axes(size_t ndim, const int* contracted, size_t* axes) {
77 size_t count = 0;
79 for (size_t i = 0; i < ndim; ++i) {
80 if (!contracted[i]) {
81 axes[count++] = i;
82 }
83 }
84 return count;
85}
86
87static lmmc_status_t lmmc_tensor_nd_contraction_shape(
88 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
89 lmmc_tensor_nd_contraction_t* contraction, size_t* out_ndim, size_t* out_dims) {
90 int a_contracted[LMMC_TENSOR_MAX_NDIM] = {0};
91 int b_contracted[LMMC_TENSOR_MAX_NDIM] = {0};
92 contraction->contract_size = 1;
93 for (size_t i = 0; i < contraction->naxes; ++i) {
94 const size_t axis_a = contraction->axes_a[i];
95 const size_t axis_b = contraction->axes_b[i];
96 if (axis_a >= a->ndim || axis_b >= b->ndim) {
98 }
99 if (a_contracted[axis_a] || b_contracted[axis_b]) {
101 }
102 if (a->dims[axis_a] != b->dims[axis_b] ||
103 !lmmc_safe_mul_size(
104 contraction->contract_size, a->dims[axis_a],
105 &contraction->contract_size)) {
107 }
108 a_contracted[axis_a] = 1;
109 b_contracted[axis_b] = 1;
110 }
111 contraction->n_a_free = lmmc_tensor_nd_free_axes(a->ndim, a_contracted, contraction->a_free_axes);
112 contraction->n_b_free = lmmc_tensor_nd_free_axes(b->ndim, b_contracted, contraction->b_free_axes);
113 const size_t free_count = contraction->n_a_free + contraction->n_b_free;
115 *out_ndim = free_count == 0 ? 1 : free_count;
116 if (*out_ndim > LMMC_TENSOR_MAX_NDIM) {
118 }
119
120 size_t j = 0;
121 for (size_t i = 0; i < contraction->n_a_free; ++i) {
122 out_dims[j++] = a->dims[contraction->a_free_axes[i]];
123 }
124 for (size_t i = 0; i < contraction->n_b_free; ++i) {
125 out_dims[j++] = b->dims[contraction->b_free_axes[i]];
126 }
127 if (free_count == 0) {
128 out_dims[0] = 1;
129 }
130 return LMMC_STATUS_OK;
131}
132
133static size_t lmmc_tensor_nd_contracted_offset(
134 const lmmc_tensor_nd_t* tensor, const size_t* free_axes, size_t n_free,
135 const size_t* out_idx, const size_t* contracted_axes, size_t naxes,
136 const size_t* contract_idx) {
137 size_t full_idx[LMMC_TENSOR_MAX_NDIM] = {0};
138 for (size_t i = 0; i < n_free; ++i) {
139 full_idx[free_axes[i]] = out_idx[i];
140 }
141 for (size_t i = 0; i < naxes; ++i) {
142 full_idx[contracted_axes[i]] = contract_idx[i];
143 }
144 return lmmc_tensor_nd_offset(tensor->ndim, tensor->strides, full_idx);
145}
146
147static void lmmc_tensor_nd_next_contracted_index(
148 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_contraction_t* contraction,
149 size_t* contract_idx) {
150 size_t ci = contraction->naxes;
152 while (ci > 0) {
153 --ci;
154 contract_idx[ci]++;
155 if (contract_idx[ci] < a->dims[contraction->axes_a[ci]]) {
156 break;
157 }
158 contract_idx[ci] = 0;
159 }
160}
161
162static void lmmc_tensor_nd_contract_element(
163 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
164 const lmmc_tensor_nd_contraction_t* contraction, size_t contract_size,
165 const size_t* out_idx, lmmc_real_t* sum) {
166 size_t contract_idx[LMMC_TENSOR_MAX_NDIM] = {0};
167 LMMC_REAL_SET_D(sum, 0.0);
168 for (size_t k = 0; k < contract_size; ++k) {
169 const size_t a_offset = lmmc_tensor_nd_contracted_offset(
170 a, contraction->a_free_axes, contraction->n_a_free, out_idx,
171 contraction->axes_a, contraction->naxes, contract_idx);
172 const size_t b_offset = lmmc_tensor_nd_contracted_offset(
173 b, contraction->b_free_axes, contraction->n_b_free,
174 out_idx + contraction->n_a_free, contraction->axes_b,
175 contraction->naxes, contract_idx);
176 lmmc_tensor_nd_add_product(sum, &a->data[a_offset], &b->data[b_offset]);
177 lmmc_tensor_nd_next_contracted_index(a, contraction, contract_idx);
178 }
179}
180
181static void lmmc_tensor_nd_execute_contraction(
182 const lmmc_tensor_nd_t* a, const lmmc_tensor_nd_t* b,
183 const lmmc_tensor_nd_contraction_t* contraction, lmmc_tensor_nd_t* out) {
184 size_t out_idx[LMMC_TENSOR_MAX_NDIM] = {0};
185 do {
186 lmmc_real_t sum;
187 LMMC_REAL_INIT(&sum);
188 lmmc_tensor_nd_contract_element(
189 a, b, contraction, contraction->contract_size, out_idx, &sum);
190 const size_t offset = lmmc_tensor_nd_offset(out->ndim, out->strides, out_idx);
191 LMMC_REAL_SET(&out->data[offset], &sum);
192 LMMC_REAL_CLEAR(&sum);
193 } while (lmmc_tensor_nd_next_index(out->ndim, out->dims, out_idx));
194}
195
197 const size_t* axes_a, const size_t* axes_b, size_t naxes, lmmc_tensor_nd_t* out) {
199 lmmc_tensor_nd_t a_snapshot;
200 lmmc_tensor_nd_t b_snapshot;
201 lmmc_tensor_nd_t result = {0};
202 size_t out_ndim;
203 size_t out_dims[LMMC_TENSOR_MAX_NDIM];
204 contraction.naxes = naxes;
205 contraction.axes_a = axes_a;
206 contraction.axes_b = axes_b;
207 lmmc_status_t st = lmmc_tensor_nd_validate_contraction(a, b, &contraction, out);
208 if (st == LMMC_STATUS_OK) {
209 st = lmmc_tensor_nd_contraction_shape(a, b, &contraction, &out_ndim, out_dims);
210 }
211 if (st != LMMC_STATUS_OK) {
212 return st;
213 }
214 if (out == a) {
215 a_snapshot = *a;
216 a = &a_snapshot;
217 }
218 if (out == b) {
219 b_snapshot = *b;
220 b = &b_snapshot;
221 }
222 st = lmmc_tensor_nd_allocate_result(out_ndim, out_dims, &result);
223 if (st != LMMC_STATUS_OK) {
224 return st;
225 }
226 lmmc_tensor_nd_execute_contraction(a, b, &contraction, &result);
227 *out = result;
228 return LMMC_STATUS_OK;
229}
230
231static lmmc_status_t lmmc_tensor_nd_validate_mode_product(
232 const lmmc_tensor_nd_t* t, const lmmc_mat_t* mat, size_t mode,
233 const lmmc_tensor_nd_t* out) {
234 if (!lmmc_tensor_nd_descriptor_is_valid(t) ||
235 !lmmc_mat_descriptor_is_valid(mat) || out == NULL) {
237 }
238 if (out == t && t->owns_data) {
240 }
241 if (mode >= t->ndim || mat->cols != t->dims[mode]) {
243 }
244 return LMMC_STATUS_OK;
245}
246
247static void lmmc_tensor_nd_mode_product_element(
248 const lmmc_tensor_nd_t* t, const lmmc_mat_t* mat, size_t mode,
249 const size_t* out_idx, lmmc_real_t* sum) {
250 const size_t j_dim = t->dims[mode];
251 LMMC_REAL_SET_D(sum, 0.0);
252 for (size_t i = 0; i < j_dim; ++i) {
254 size_t src_offset = 0;
255 for (size_t d = 0; d < t->ndim; ++d) {
256 if (d == mode) {
257 src_offset += i * t->strides[d];
258 } else {
259 src_offset += out_idx[d] * t->strides[d];
260 }
261 }
262 lmmc_tensor_nd_add_product(
263 sum, &mat->data[out_idx[mode] * mat->stride + i], &t->data[src_offset]);
264 }
265}
266
267static void lmmc_tensor_nd_execute_mode_product(
268 const lmmc_tensor_nd_t* t, const lmmc_mat_t* mat, size_t mode,
269 lmmc_tensor_nd_t* out) {
271 size_t out_idx[LMMC_TENSOR_MAX_NDIM] = {0};
272 do {
273 lmmc_real_t sum;
274 LMMC_REAL_INIT(&sum);
275 lmmc_tensor_nd_mode_product_element(t, mat, mode, out_idx, &sum);
276 const size_t offset = lmmc_tensor_nd_offset(out->ndim, out->strides, out_idx);
277 LMMC_REAL_SET(&out->data[offset], &sum);
278 LMMC_REAL_CLEAR(&sum);
279 } while (lmmc_tensor_nd_next_index(out->ndim, out->dims, out_idx));
280}
281
283 size_t mode, lmmc_tensor_nd_t* out) {
284 lmmc_tensor_nd_t input_snapshot;
285 lmmc_tensor_nd_t result = {0};
286 size_t out_dims[LMMC_TENSOR_MAX_NDIM];
287 lmmc_status_t st = lmmc_tensor_nd_validate_mode_product(t, mat, mode, out);
288 if (st != LMMC_STATUS_OK) {
289 return st;
290 }
291 for (size_t i = 0; i < t->ndim; ++i) {
292 out_dims[i] = i == mode ? mat->rows : t->dims[i];
293 }
294 if (out == t) {
295 input_snapshot = *t;
296 t = &input_snapshot;
297 }
298 st = lmmc_tensor_nd_allocate_result(t->ndim, out_dims, &result);
299 if (st != LMMC_STATUS_OK) {
300 return st;
301 }
302 lmmc_tensor_nd_execute_mode_product(t, mat, mode, &result);
303 *out = result;
304 return LMMC_STATUS_OK;
305}
#define LMMC_REAL_CLEAR(x)
释放一个 lmmc_real_t 变量(POD 类型为空操作)。
#define LMMC_REAL_INIT(x)
初始化一个 lmmc_real_t 变量(POD 类型为空操作)。
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_REAL_SET(dst, src)
将一个 lmmc_real_t 赋值到另一个:*dst = *src 。
#define LMMC_REAL_SET_D(dst, dbl)
将一个 double 字面量赋值到 lmmc_real_t 。
lmmc_real_t * data
行优先存储的稠密矩阵。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_INVALID_ARGUMENT
lmmc_status_t lmmc_tensor_nd_mode_n_product(const lmmc_tensor_nd_t *t, const lmmc_mat_t *mat, size_t mode, lmmc_tensor_nd_t *out)
模-n 乘积:沿指定模与矩阵收缩.
lmmc_status_t lmmc_tensor_nd_contract(const lmmc_tensor_nd_t *a, const lmmc_tensor_nd_t *b, const size_t *axes_a, const size_t *axes_b, size_t naxes, lmmc_tensor_nd_t *out)
张量收缩:沿匹配轴对求和.
#define LMMC_TENSOR_MAX_NDIM
支持的最大张量维度.
lmmc_real_t * data
size_t dims[LMMC_TENSOR_MAX_NDIM]
size_t strides[LMMC_TENSOR_MAX_NDIM]
N-D 张量结构.
N 维张量结果存储、索引遍历与乘积累加。
size_t b_free_axes[LMMC_TENSOR_MAX_NDIM]
size_t a_free_axes[LMMC_TENSOR_MAX_NDIM]