|
33 | 33 | #include <aclnnop/aclnn_avgpool2d.h> |
34 | 34 | #include <aclnnop/aclnn_batch_matmul.h> |
35 | 35 | #include <aclnnop/aclnn_cast.h> |
| 36 | +#include <aclnnop/aclnn_cat.h> |
36 | 37 | #include <aclnnop/aclnn_clamp.h> |
| 38 | +#include <aclnnop/aclnn_clipped_swiglu.h> |
37 | 39 | #include <aclnnop/aclnn_constant_pad_nd.h> |
38 | 40 | #include <aclnnop/aclnn_convolution.h> |
39 | 41 | #include <aclnnop/aclnn_copy.h> |
@@ -211,6 +213,68 @@ void ggml_cann_swiglu(ggml_backend_cann_context & ctx, ggml_tensor * dst) { |
211 | 213 | GGML_CANN_CALL_ACLNN_OP(ctx, SwiGlu, acl_src.get(), (int64_t)2, acl_dst.get()); |
212 | 214 | } |
213 | 215 |
|
| 216 | +// SWIGLU_OAI (gpt-oss MoE FFN). Per element, with A the gate half and B the |
| 217 | +// value half: |
| 218 | +// A = min(A, limit); B = clamp(B, -limit, limit); |
| 219 | +// y = A * sigmoid(alpha * A) * (B + 1) |
| 220 | +// This is exactly aclnnClippedSwiglu (bias = 1, interleaved = false, A = first |
| 221 | +// half, B = second half). aclnnClippedSwiglu takes a single tensor and splits |
| 222 | +// it, so the two halves are concatenated as [gate; value] along ne[0] first. |
| 223 | +// `swapped` reverses which half is the gate. |
| 224 | +void ggml_cann_swiglu_oai(ggml_backend_cann_context & ctx, ggml_tensor * dst) { |
| 225 | + ggml_tensor * src0 = dst->src[0]; |
| 226 | + ggml_tensor * src1 = dst->src[1]; |
| 227 | + |
| 228 | + GGML_ASSERT(ggml_is_contiguous_1(src0)); |
| 229 | + GGML_ASSERT(ggml_is_contiguous_1(dst)); |
| 230 | + GGML_ASSERT(src0->type == GGML_TYPE_F32); |
| 231 | + GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| 232 | + |
| 233 | + const int32_t swapped = ggml_get_op_params_i32(dst, 1); |
| 234 | + const float alpha = ggml_get_op_params_f32(dst, 2); |
| 235 | + const float limit = ggml_get_op_params_f32(dst, 3); |
| 236 | + |
| 237 | + // acl_a/acl_b are the gate/value candidates; swapped flips which is the gate. |
| 238 | + acl_tensor_ptr acl_a; |
| 239 | + acl_tensor_ptr acl_b; |
| 240 | + if (src1 == nullptr) { |
| 241 | + GGML_ASSERT(src0->ne[0] % 2 == 0); |
| 242 | + int64_t ne_h[GGML_MAX_DIMS] = { src0->ne[0] / 2, src0->ne[1], src0->ne[2], src0->ne[3] }; |
| 243 | + size_t nb_h[GGML_MAX_DIMS] = { (size_t)src0->nb[0], (size_t)src0->nb[1], |
| 244 | + (size_t)src0->nb[2], (size_t)src0->nb[3] }; |
| 245 | + const size_t off = ne_h[0] * ggml_element_size(src0); |
| 246 | + acl_a = ggml_cann_create_tensor(src0, ne_h, nb_h, GGML_MAX_DIMS, ACL_FORMAT_ND, 0); // first half |
| 247 | + acl_b = ggml_cann_create_tensor(src0, ne_h, nb_h, GGML_MAX_DIMS, ACL_FORMAT_ND, off); // second half |
| 248 | + } else { |
| 249 | + GGML_ASSERT(ggml_is_contiguous_1(src1)); |
| 250 | + GGML_ASSERT(src0->type == src1->type); |
| 251 | + acl_a = ggml_cann_create_tensor(src0); |
| 252 | + acl_b = ggml_cann_create_tensor(src1); |
| 253 | + } |
| 254 | + if (swapped) { |
| 255 | + std::swap(acl_a, acl_b); // acl_a = gate (first half of the input to ClippedSwiglu) |
| 256 | + } |
| 257 | + |
| 258 | + // concat [acl_a (gate); acl_b (value)] along ne[0] -> ClippedSwiglu splits it |
| 259 | + const size_t es = ggml_element_size(dst); |
| 260 | + int64_t cat_ne[GGML_MAX_DIMS] = { dst->ne[0] * 2, dst->ne[1], dst->ne[2], dst->ne[3] }; |
| 261 | + size_t cat_nb[GGML_MAX_DIMS]; |
| 262 | + cat_nb[0] = es; |
| 263 | + for (int i = 1; i < GGML_MAX_DIMS; i++) { |
| 264 | + cat_nb[i] = cat_nb[i - 1] * cat_ne[i - 1]; |
| 265 | + } |
| 266 | + ggml_cann_pool_alloc cat_alloc(ctx.pool(), cat_nb[GGML_MAX_DIMS - 1] * cat_ne[GGML_MAX_DIMS - 1]); |
| 267 | + acl_tensor_ptr acl_x = ggml_cann_create_tensor(cat_alloc.get(), ggml_cann_type_mapping(dst->type), |
| 268 | + es, cat_ne, cat_nb, GGML_MAX_DIMS); |
| 269 | + acl_tensor_list_ptr list = ggml_cann_create_tensor_list(acl_a, acl_b); |
| 270 | + GGML_CANN_CALL_ACLNN_OP(ctx, Cat, list.get(), (int64_t)(GGML_MAX_DIMS - 1), acl_x.get()); |
| 271 | + |
| 272 | + acl_tensor_ptr acl_out = ggml_cann_create_tensor(dst); |
| 273 | + GGML_CANN_CALL_ACLNN_OP(ctx, ClippedSwiglu, acl_x.get(), (const aclTensor *) nullptr, |
| 274 | + (int64_t)(GGML_MAX_DIMS - 1), (double) alpha, (double) limit, (double) 1.0, |
| 275 | + /*interleaved*/ false, acl_out.get()); |
| 276 | +} |
| 277 | + |
214 | 278 | // Fused GeGLU using aclnnGeGluV3: splits input along ne[0] (CANN last dim), |
215 | 279 | // activates the LEFT half with GELU, multiplies by right half. |
216 | 280 | // approximate: 0=tanh, 1=none(erf). activateLeft=true matches GGML convention. |
@@ -3550,6 +3614,71 @@ void ggml_cann_geglu_quick(ggml_backend_cann_context & ctx, ggml_tensor * dst) { |
3550 | 3614 | ggml_cann_op_unary_gated(gelu_quick_fn, ctx, dst); |
3551 | 3615 | } |
3552 | 3616 |
|
| 3617 | +/** |
| 3618 | + * @brief Adds expert-specific rows to a tensor (MoE) using the CANN backend. |
| 3619 | + * |
| 3620 | + * Implements GGML_OP_ADD_ID. For each row (i2, i1) of src0, the row of src1 |
| 3621 | + * selected by ids[i1, i2] is added element-wise. Used to scatter expert |
| 3622 | + * outputs back onto the hidden state in Mixture-of-Experts models. |
| 3623 | + * |
| 3624 | + * Dimensions: |
| 3625 | + * - src0 (a) : [ne0, ne1, ne2, ne3], F32 |
| 3626 | + * - src1 (b) : [ne0, ne11], F32 (ne11 rows to select from) |
| 3627 | + * - ids : [ne1, ne2], I32 |
| 3628 | + * - dst : same shape as src0 |
| 3629 | + * |
| 3630 | + * For each i2, IndexSelect gathers ne1 rows from src1 according to ids[:, i2] |
| 3631 | + * into a [ne0, ne1] scratch buffer, which is then added to every matching |
| 3632 | + * (i2, i3) slice of src0. |
| 3633 | + */ |
| 3634 | +void ggml_cann_add_id(ggml_backend_cann_context & ctx, ggml_tensor * dst) { |
| 3635 | + ggml_tensor * src0 = dst->src[0]; // a: [ne0, ne1, ne2, 1] |
| 3636 | + ggml_tensor * src1 = dst->src[1]; // b: [ne0, ne11] |
| 3637 | + ggml_tensor * ids = dst->src[2]; // ids: [ne1, ne2] |
| 3638 | + |
| 3639 | + GGML_ASSERT(dst->type == GGML_TYPE_F32); |
| 3640 | + GGML_ASSERT(src0->type == GGML_TYPE_F32); |
| 3641 | + GGML_ASSERT(src1->type == GGML_TYPE_F32); |
| 3642 | + GGML_ASSERT(ids->type == GGML_TYPE_I32); |
| 3643 | + GGML_ASSERT(src0->ne[3] == 1); |
| 3644 | + GGML_ASSERT(dst->ne[3] == 1); |
| 3645 | + |
| 3646 | + const int64_t ne0 = src0->ne[0]; |
| 3647 | + const int64_t ne1 = src0->ne[1]; |
| 3648 | + const int64_t ne2 = src0->ne[2]; |
| 3649 | + |
| 3650 | + // scratch buffer holding the selected src1 rows: [ne0, ne1] |
| 3651 | + ggml_cann_pool_alloc select_allocator(ctx.pool(), ne0 * ne1 * ggml_element_size(src1)); |
| 3652 | + void * select_ptr = select_allocator.get(); |
| 3653 | + |
| 3654 | + int64_t select_ne[2] = { ne0, ne1 }; |
| 3655 | + size_t select_nb[2]; |
| 3656 | + select_nb[0] = src1->nb[0]; |
| 3657 | + select_nb[1] = select_nb[0] * ne0; |
| 3658 | + |
| 3659 | + int64_t slice_ne[2] = { ne0, ne1 }; |
| 3660 | + size_t a_slice_nb[2] = { src0->nb[0], src0->nb[1] }; |
| 3661 | + size_t d_slice_nb[2] = { dst->nb[0], dst->nb[1] }; |
| 3662 | + |
| 3663 | + acl_tensor_ptr acl_b = ggml_cann_create_tensor(src1, src1->ne, src1->nb, 2); |
| 3664 | + |
| 3665 | + for (int64_t i2 = 0; i2 < ne2; i2++) { |
| 3666 | + // ids[:, i2]: ne1 indices into b's rows |
| 3667 | + acl_tensor_ptr select_index = |
| 3668 | + ggml_cann_create_tensor(ids, ids->ne, ids->nb, 1, ACL_FORMAT_ND, i2 * ids->nb[1]); |
| 3669 | + acl_tensor_ptr select_out = ggml_cann_create_tensor(select_ptr, ggml_cann_type_mapping(src1->type), |
| 3670 | + ggml_element_size(src1), select_ne, select_nb, 2); |
| 3671 | + // gather ne1 rows from b along its row axis (ACL dim 0 == ggml ne[1]) |
| 3672 | + GGML_CANN_CALL_ACLNN_OP(ctx, IndexSelect, acl_b.get(), 0, select_index.get(), select_out.get()); |
| 3673 | + |
| 3674 | + acl_tensor_ptr acl_a_slice = |
| 3675 | + ggml_cann_create_tensor(src0, slice_ne, a_slice_nb, 2, ACL_FORMAT_ND, i2 * src0->nb[2]); |
| 3676 | + acl_tensor_ptr acl_d_slice = |
| 3677 | + ggml_cann_create_tensor(dst, slice_ne, d_slice_nb, 2, ACL_FORMAT_ND, i2 * dst->nb[2]); |
| 3678 | + aclnn_add(ctx, acl_a_slice.get(), select_out.get(), acl_d_slice.get()); |
| 3679 | + } |
| 3680 | +} |
| 3681 | + |
3553 | 3682 | /** |
3554 | 3683 | * @brief Performs expert-specific matrix multiplication (MoE) with |
3555 | 3684 | * floating-point precision using the CANN backend. |
|
0 commit comments