#include "../node_context.h" #include "../op_table.h" #include "../utils.h" #include #include #include #include #include #include #include #include namespace ov { namespace frontend { namespace ggml { namespace op { OutputVector translate_glu_geglu(const NodeContext & context) { num_inputs_check(context, 1, 2); ov::Output src0; ov::Output src1; if (context.get_input_size() == 2) { // Inputs may be VIEW slices of a combined gate_up tensor (MoE experts): // resolve them so each half has its real sliced shape, not the base tensor. src0 = process_view_input_new(context, 0); src1 = process_view_input_new(context, 1); } else { // GGML splits along ne[0] (OV last axis) using floor division: nc = ne[0] / 2. // Both halves are nc elements; if the dimension is odd, the last element is dropped. // Use Slice instead of Split to handle odd dimensions correctly. // Resolve a VIEW input (e.g. non-contiguous slice) to its real shape first. auto combined = process_view_input_new(context, 0); auto combined_shape = combined.get_partial_shape(); int64_t last_dim_val = combined_shape[combined_shape.rank().get_length() - 1].get_length(); int64_t nc = last_dim_val / 2; auto axis = ov::op::v0::Constant::create(ov::element::i64, {1}, {-1}); auto step = ov::op::v0::Constant::create(ov::element::i64, {1}, {1}); auto start0 = ov::op::v0::Constant::create(ov::element::i64, {1}, {0}); auto stop0 = ov::op::v0::Constant::create(ov::element::i64, {1}, {nc}); auto start1 = ov::op::v0::Constant::create(ov::element::i64, {1}, {nc}); auto stop1 = ov::op::v0::Constant::create(ov::element::i64, {1}, {2 * nc}); src0 = std::make_shared(combined, start0, stop0, step, axis); src1 = std::make_shared(combined, start1, stop1, step, axis); } int32_t * params = context.get_output_op_params(); const int32_t swapped = params[1]; if (swapped) { std::swap(src0, src1); } if (context.is_static()) { // TODO: Temporary solution for NPU accuracy issue due to fp16 overflow // To be removed once permanent solution is implemented // Justification: // For |x| > 5, GELU(x) ≈ max(x, 0) (behaves like ReLU) // So Clamp(-10, 10) only affects values where GELU would return ≈ x anyway. // The only loss: values > 10 get mapped to 10 instead of x. // In practice, FFN intermediates rarely exceed 10 after GEGLU gating. src0 = std::make_shared(src0, -10.0, 10.0); } auto gelu = std::make_shared(src0); auto res = std::make_shared(gelu, src1); return rename_outputs_with_suffix({res}, context.get_name()); } } // namespace op } // namespace ggml } // namespace frontend } // namespace ov