char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Zijun YuOpenVINO: Update OV to 2026.3.1, whisper.cpp support, Qwen3.5 on NPU, and new ops (#27843)511f9c137

char/aliceai-t5-moe
43.9 KiB886 linesraw
1#include "utils.h"
2
3#include "ggml-impl.h"
4
5#include <cmath>
6#include <cstddef>
7#include <ctime>
8#include <memory>
9#include <openvino/op/add.hpp>
10#include <openvino/op/clamp.hpp>
11#include <openvino/op/convert.hpp>
12#include <openvino/op/cos.hpp>
13#include <openvino/op/divide.hpp>
14#include <openvino/op/gather.hpp>
15#include <openvino/op/maximum.hpp>
16#include <openvino/op/multiply.hpp>
17#include <openvino/op/reshape.hpp>
18#include <openvino/op/shape_of.hpp>
19#include <openvino/op/sin.hpp>
20#include <openvino/op/slice.hpp>
21#include <openvino/op/split.hpp>
22#include <openvino/op/squeeze.hpp>
23#include <openvino/op/subtract.hpp>
24#include <openvino/op/transpose.hpp>
25#include <string>
26
27namespace ov {
28namespace frontend {
29namespace ggml {
30
31std::string getCurrentTime() {
32    std::time_t now = std::time(nullptr);
33    char buf[100];
34    std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
35    return buf;
36}
37
38void num_inputs_check(const NodeContext & context, size_t min_inputs, size_t max_inputs) {
39    auto input_size = context.get_input_size();
40    FRONT_END_OP_CONVERSION_CHECK(input_size >= min_inputs, "Got less inputs than expected");
41    FRONT_END_OP_CONVERSION_CHECK(input_size <= max_inputs, "Got more inputs than expected");
42}
43
44int non_cont_dim(std::vector<size_t> ne, std::vector<size_t> nb) {
45    int dim = nb.size() - 1;
46    size_t bytes = nb[dim];
47    for (int i = dim; i > 0; i--) {
48        bytes *= ne[i];
49        if (bytes != nb[i - 1]) {
50            return i;
51        }
52    }
53    return 0;
54}
55
56std::shared_ptr<ov::Node> get_dimensions(const std::shared_ptr<ov::op::v3::ShapeOf> & shape,
57                                         const std::vector<int> & dims) {
58    using namespace ov::op;
59    const auto zero = v0::Constant::create(ov::element::i32, ov::Shape{}, {0});
60    const auto dims_const = v0::Constant::create(ov::element::i32, ov::Shape{dims.size()}, dims);
61    return std::make_shared<v8::Gather>(shape, dims_const, zero);
62}
63
64std::shared_ptr<ov::Node> get_dimensions(const std::shared_ptr<ov::Node> & node, const std::vector<int> & dims) {
65    return get_dimensions(std::make_shared<ov::op::v3::ShapeOf>(node), dims);
66}
67
68OutputVector rename_outputs_with_suffix(const OutputVector & outputs, const std::string & suffix) {
69    for (const auto & output : outputs) {
70        auto node = output.get_node_shared_ptr();
71        std::string name = node->get_friendly_name();
72        name += "_";
73        name += suffix;
74        node->set_friendly_name(name);
75        // Uncomment to dump every node's inferred shape (used to hunt down dynamic dims on NPU).
76        // std::cout << name << "  " << output.get_partial_shape() << std::endl;
77    }
78    return outputs;
79}
80
81namespace {
82ov::Output<ov::Node> rope_yarn_ramp_mix(int n_dims, const float corr_dims[2], float ext_factor) {
83    int half_n_dims = n_dims / 2;
84    std::vector<float> dim_ids_vec(half_n_dims);
85    std::iota(dim_ids_vec.begin(), dim_ids_vec.end(), 0);
86    auto dim_ids = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, (size_t) half_n_dims}, dim_ids_vec);
87    auto corr_low = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, 1}, {corr_dims[0]});
88    auto corr_high = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, 1}, {corr_dims[1]});
89    auto denom = std::make_shared<ov::op::v1::Maximum>(
90        std::make_shared<ov::op::v1::Subtract>(corr_high, corr_low),
91        ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, 1}, {0.001f}));
92    auto ramp_y =
93        std::make_shared<ov::op::v1::Divide>(std::make_shared<ov::op::v1::Subtract>(dim_ids, corr_low), denom);
94    auto ramp_clamped = std::make_shared<ov::op::v0::Clamp>(ramp_y, 0.0f, 1.0f);
95    // rope_yarn_ramp returns (1 - clamp(y)), so invert before scaling
96    auto one = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, 1}, {1.0f});
97    auto ramp_inverted = std::make_shared<ov::op::v1::Subtract>(one, ramp_clamped);
98    auto ext_factor_node = ov::op::v0::Constant::create(ov::element::f32, Shape{}, {ext_factor});
99    auto ramp_mix = std::make_shared<ov::op::v1::Multiply>(ramp_inverted, ext_factor_node);
100    return ramp_mix;
101}
102
103float ggml_rope_yarn_corr_dim(int n_dims, int n_ctx_orig, float n_rot, float base) {
104#ifndef M_PI
105#    define M_PI 3.14159265358979323846
106#endif
107    return n_dims * logf(n_ctx_orig / (n_rot * 2 * (float) M_PI)) / (2 * logf(base));
108}
109
110void ggml_rope_yarn_corr_dims(int n_dims,
111                              int n_ctx_orig,
112                              float freq_base,
113                              float beta_fast,
114                              float beta_slow,
115                              float dims[2]) {
116    float start = floorf(ggml_rope_yarn_corr_dim(n_dims, n_ctx_orig, beta_fast, freq_base));
117    float end = ceilf(ggml_rope_yarn_corr_dim(n_dims, n_ctx_orig, beta_slow, freq_base));
118    dims[0] = std::max(0.0f, start);
119    dims[1] = std::min(static_cast<float>(n_dims - 1), end);
120}
121}  // namespace
122
123std::pair<ov::Output<Node>, ov::Output<Node>> make_sin_cos(int32_t * rope_params,
124                                                           std::shared_ptr<ov::Node> inp_pos,
125                                                           std::shared_ptr<ov::Node> rope_freqs_weight,
126                                                           bool imrope,
127                                                           bool stateful) {
128    if (stateful) {
129        inp_pos =
130            std::make_shared<ov::op::v0::Squeeze>(inp_pos, ov::op::v0::Constant::create(ov::element::i64, {1}, {0}));
131        inp_pos = std::make_shared<ov::op::v0::Convert>(inp_pos, ov::element::f32);
132        auto pos_perm =
133            std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{3}, std::vector<int64_t>{2, 1, 0});
134        inp_pos = std::make_shared<ov::op::v1::Transpose>(inp_pos, pos_perm);
135    } else if (imrope) {
136        inp_pos = std::make_shared<ov::op::v0::Convert>(inp_pos, ov::element::f32);
137        auto pos_shape = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{5}, {0, 0, 0, 4, -1});
138        inp_pos = std::make_shared<ov::op::v1::Reshape>(inp_pos, pos_shape, true);
139        auto pos_transpose_shape =
140            std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{5}, std::vector<int64_t>{0, 1, 2, 4, 3});
141        inp_pos = std::make_shared<ov::op::v1::Transpose>(inp_pos, pos_transpose_shape);
142    } else {
143        inp_pos = std::make_shared<ov::op::v0::Convert>(inp_pos, ov::element::f32);
144        auto pos_perm =
145            std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{4}, std::vector<int64_t>{0, 3, 1, 2});
146        inp_pos = std::make_shared<ov::op::v1::Transpose>(inp_pos, pos_perm);
147    }
148
149    float freq_base;
150    float freq_scale;
151    float ext_factor;
152    float attn_factor;
153    float beta_fast;
154    float beta_slow;
155    const int n_dims = rope_params[1];
156    const size_t n_dims_half = n_dims >> 1;
157    const int n_ctx_orig = rope_params[4];
158    memcpy(&freq_base, rope_params + 5, sizeof(float));
159    memcpy(&freq_scale, rope_params + 6, sizeof(float));
160    memcpy(&ext_factor, rope_params + 7, sizeof(float));
161    memcpy(&attn_factor, rope_params + 8, sizeof(float));
162    memcpy(&beta_fast, rope_params + 9, sizeof(float));
163    memcpy(&beta_slow, rope_params + 10, sizeof(float));
164
165    const float theta_scale = powf(freq_base, -2.0f / n_dims);
166
167    std::vector<float> factor(n_dims_half);
168
169    Output<Node> freq_factors;
170
171    Output<Node> theta;
172    float mscale = attn_factor;
173    if (imrope) {
174        std::vector<int64_t> gather_indices(n_dims_half);
175        for (size_t j = 0; j < n_dims_half; j++) {
176            gather_indices[j] = j % 3;
177            factor[j] = std::pow(theta_scale, j);
178        }
179        auto gather_indices_const =
180            std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{n_dims_half}, gather_indices);
181        auto gather_axis = ov::op::v0::Constant::create(ov::element::i32, ov::Shape{}, {4});
182        inp_pos = std::make_shared<ov::op::v8::Gather>(inp_pos, gather_indices_const, gather_axis);
183        auto factor_const = std::make_shared<ov::op::v0::Constant>(ov::element::f32, ov::Shape{n_dims_half}, factor);
184        theta = std::make_shared<ov::op::v1::Multiply>(inp_pos, factor_const);
185    } else {
186        float corr_dims[2];
187        ggml_rope_yarn_corr_dims(n_dims, n_ctx_orig, freq_base, beta_fast, beta_slow, corr_dims);
188        factor[0] = 1.0f;
189        for (size_t i = 1; i < factor.size(); i++) {
190            factor[i] = theta_scale * factor[i - 1];
191        }
192        if (stateful) {
193            freq_factors =
194                std::make_shared<ov::op::v0::Constant>(ov::element::f32, ov::Shape{1, 1, factor.size()}, factor);
195        } else {
196            freq_factors =
197                std::make_shared<ov::op::v0::Constant>(ov::element::f32, ov::Shape{1, 1, 1, factor.size()}, factor);
198        }
199        if (rope_freqs_weight) {
200            Output<Node> rope_factors = std::make_shared<ov::op::v8::Slice>(
201                rope_freqs_weight,
202                ov::op::v0::Constant::create(ov::element::i64, {1}, {0}),
203                ov::op::v0::Constant::create(ov::element::i64, {1}, {(int64_t) n_dims_half}),
204                ov::op::v0::Constant::create(ov::element::i64, {1}, {1}),
205                ov::op::v0::Constant::create(ov::element::i64, {1}, {rope_freqs_weight->get_output_partial_shape(0).rank().get_length() - 1}));
206            if (stateful) {
207                rope_factors = std::make_shared<ov::op::v1::Reshape>(
208                    rope_factors,
209                    ov::op::v0::Constant::create(ov::element::i64, {3}, {(int64_t) 1, (int64_t) 1, (int64_t) n_dims_half}),
210                    false);
211            } else {
212                rope_factors = std::make_shared<ov::op::v1::Reshape>(
213                    rope_factors,
214                    ov::op::v0::Constant::create(ov::element::i64, {4}, {(int64_t) 1, (int64_t) 1, (int64_t) 1, (int64_t) n_dims_half}),
215                    false);
216            }
217            freq_factors = std::make_shared<ov::op::v1::Divide>(freq_factors, rope_factors);
218        }
219
220        auto theta_extrap = std::make_shared<ov::op::v1::Multiply>(freq_factors, inp_pos);
221        auto theta_interp = std::make_shared<ov::op::v1::Multiply>(
222            theta_extrap, ov::op::v0::Constant::create(ov::element::f32, {1}, {freq_scale}));
223
224        if (ext_factor == 0.0f) {
225            theta = theta_interp;
226        } else {
227            auto ramp_mix = rope_yarn_ramp_mix(n_dims, corr_dims, ext_factor);
228            Output<Node> one;
229            if (stateful) {
230                one = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1}, {1.0f});
231            } else {
232                one = ov::op::v0::Constant::create(ov::element::f32, Shape{1, 1, 1, 1}, {1.0f});
233            }
234            auto one_minus_ramp = std::make_shared<ov::op::v1::Subtract>(one, ramp_mix);
235
236            theta =
237                std::make_shared<ov::op::v1::Add>(std::make_shared<ov::op::v1::Multiply>(theta_interp, one_minus_ramp),
238                                                  std::make_shared<ov::op::v1::Multiply>(theta_extrap, ramp_mix));
239            mscale *= (1.0f + 0.1f * std::log(1.0f / freq_scale));
240        }
241    }
242
243    Output<Node> cos_theta = std::make_shared<ov::op::v0::Cos>(theta);
244    Output<Node> sin_theta = std::make_shared<ov::op::v0::Sin>(theta);
245
246    if (!imrope) {
247        auto mscale_node = ov::op::v0::Constant::create(ov::element::f32, Shape{}, {mscale});
248
249        cos_theta = std::make_shared<ov::op::v1::Multiply>(cos_theta, mscale_node);
250        sin_theta = std::make_shared<ov::op::v1::Multiply>(sin_theta, mscale_node);
251    }
252
253    return std::make_pair(sin_theta, cos_theta);
254}
255
256ov::Output<ov::Node> process_view_input(const NodeContext & context, int input_index, int slice_len, int axis) {
257    // Only works for VIEW operations that does a non-strided slice with optinal reshape on the slice result.
258    // The function only does the slice part, the reshape (if any) should be handled by the caller.
259    // Default axis is -1, which means slicing the last dimension.
260    // If the VIEW reshapes the result, `slice_len` should be provided
261    auto input = context.get_input(input_index);
262    auto * op_params = (size_t *) context.get_input_op_params(input_index);
263    auto src_stride = context.get_input_stride(input_index);
264
265    int64_t slice_start = op_params[0] / src_stride[3];
266    if (slice_len == 0) {
267        slice_len = context.get_input_shape(input_index)[3].get_length();
268    }
269    int64_t slice_end = slice_start + slice_len;
270
271    auto begin = ov::op::v0::Constant::create(ov::element::i64, {1}, {slice_start});
272    auto end = ov::op::v0::Constant::create(ov::element::i64, {1}, {slice_end});
273    auto stride = ov::op::v0::Constant::create(ov::element::i64, {1}, {1});
274    ov::Output<ov::Node> axes;
275    if (axis == -1) {
276        axes = ov::op::v0::Constant::create(ov::element::i64, {1}, {context.is_stateful() ? 2 : 3});
277    } else {
278        axes = ov::op::v0::Constant::create(ov::element::i64, {1}, {axis});
279    }
280    auto sliced = std::make_shared<ov::op::v8::Slice>(input, begin, end, stride, axes);
281    return sliced;
282}
283
284ov::Output<ov::Node> process_view_input_new(const NodeContext & context, int input_index) {
285    auto input = context.get_input(input_index);
286
287    // Check if this input has view inputs
288    size_t view_input_size = context.get_view_input_size(input_index);
289    if (view_input_size == 0) {
290        // No view inputs, return the input as is
291        return input;
292    }
293
294    // If translate_view already resolved this VIEW (produced a Slice), the input
295    // will already have the expected shape — skip re-slicing.
296    //
297    // Two notions of "matches" are accepted per axis:
298    //   - both dims static and equal, OR
299    //   - both dims dynamic.
300    // The dynamic case matters for the MoE expert-plane views: translate_view now emits a
301    // DYNAMIC-token slice (so the token dim is not frozen). An all-static-only check would
302    // see the dynamic token dim, decide the shapes "don't match", and fall through to
303    // re-slice/flatten the already-resolved view (a Reshape to the full flattened
304    // n_expert_used*n_embd tail, which then conflicts with the single-plane input). Treat a
305    // dynamic-vs-dynamic axis as matching so the already-resolved view is reused as-is.
306    //
307    // A third case matters for split-model MoE fragments: translate_view resolves the
308    // expert-plane view against the fragment's INPUT parameter. When the graph is split
309    // the token axis of that parameter may already be concrete (static n_tokens) even
310    // though get_view_input_ov_shape() still reports it as dynamic (-1). The resolved
311    // view is then static [1,1,n_tokens,n_embd] while `expected` is [1,1,?,n_embd].
312    // An "expected dynamic, actual static" axis is a valid concretization of the SAME
313    // resolved view, so treat it as matching too. Falling through to process_single_view
314    // here would re-slice/re-flatten the already-resolved single-plane view against the
315    // recorded (multi-plane) source strides and emit a constant-target Reshape whose baked
316    // dims no longer divide the concretized input -> "dimensions do not evenly divide".
317    auto expected_ov_shape = context.get_view_input_ov_shape(input_index, 0);
318    auto actual_shape = input.get_partial_shape();
319    if (expected_ov_shape.rank().is_static() && actual_shape.rank().is_static() &&
320        expected_ov_shape.rank() == actual_shape.rank()) {
321        bool shapes_match = true;
322        for (int64_t i = 0; i < expected_ov_shape.rank().get_length(); ++i) {
323            const bool both_dynamic = expected_ov_shape[i].is_dynamic() && actual_shape[i].is_dynamic();
324            const bool both_static_equal = expected_ov_shape[i].is_static() && actual_shape[i].is_static() &&
325                                           expected_ov_shape[i] == actual_shape[i];
326            // expected dynamic, actual static: the resolved view already carries the
327            // concrete size for this fragment; reuse it rather than re-materializing.
328            const bool expected_dyn_actual_static = expected_ov_shape[i].is_dynamic() && actual_shape[i].is_static();
329            if (!both_dynamic && !both_static_equal && !expected_dyn_actual_static) {
330                shapes_match = false;
331                break;
332            }
333        }
334        if (shapes_match) {
335            return input;
336        }
337    }
338
339    // In static mode, use Split instead of Slice for single-dimension reductions.
340    // This ensures NPUW's FOLD doesn't parametrize per-layer slice indices (which
341    // would introduce dynamic shapes). A shared Split node sits outside the repeated
342    // subgraph boundary; each layer receives one of its output ports.
343    if (context.is_static() && view_input_size == 1) {
344        auto view_stride_v = context.get_view_input_stride(input_index, 0);
345        auto view_src_stride_v = context.get_view_input_src_stride(input_index, 0);
346        auto view_ggml_shape = context.get_view_input_ggml_shape(input_index, 0);
347        auto view_src_ggml_shape = context.get_view_input_src_ggml_shape(input_index, 0);
348        auto view_offset = context.get_view_input_offset(input_index, 0);
349        auto view_src_offset = context.get_view_input_src_offset(input_index, 0);
350
351        size_t ndims = view_ggml_shape.size();
352        std::vector<int> diff_dims;
353        if (view_src_ggml_shape.size() == ndims) {
354            for (size_t i = 0; i < ndims; ++i) {
355                if (view_ggml_shape[i] != view_src_ggml_shape[i]) {
356                    diff_dims.push_back(static_cast<int>(i));
357                }
358            }
359        }
360
361        if (diff_dims.size() == 1) {
362            int split_dim = diff_dims[0];
363            int64_t num_splits = static_cast<int64_t>(view_src_ggml_shape[split_dim]);
364            int64_t chunk_size = static_cast<int64_t>(view_ggml_shape[split_dim]);
365
366            // Only apply when slicing exactly 1 element from a multi-element dimension
367            if (chunk_size == 1 && num_splits > 1) {
368                // Check suffix strides match (dimensions after split_dim)
369                bool suffix_ok = view_stride_v.size() == view_src_stride_v.size();
370                if (suffix_ok) {
371                    for (size_t i = static_cast<size_t>(split_dim) + 1; i < ndims; ++i) {
372                        if (view_stride_v[i] != view_src_stride_v[i]) {
373                            suffix_ok = false;
374                            break;
375                        }
376                    }
377                }
378
379                if (suffix_ok && view_src_stride_v[split_dim] > 0) {
380                    size_t relative_offset = view_offset >= view_src_offset ? view_offset - view_src_offset : 0;
381                    int64_t split_index = static_cast<int64_t>(relative_offset / view_src_stride_v[split_dim]);
382
383                    if (split_index >= 0 && split_index < num_splits) {
384                        auto src_node = input.get_node_shared_ptr();
385                        std::string rt_key = "split_dim_" + std::to_string(split_dim);
386                        auto & rt_info = src_node->get_rt_info();
387
388                        if (rt_info.find(rt_key) == rt_info.end()) {
389                            auto axis_const =
390                                ov::op::v0::Constant::create(ov::element::i64, {}, {static_cast<int64_t>(split_dim)});
391                            auto split_node =
392                                std::make_shared<ov::op::v1::Split>(input, axis_const, static_cast<size_t>(num_splits));
393                            split_node->set_friendly_name(src_node->get_friendly_name() + "_split");
394                            rt_info[rt_key] = split_node;
395                        }
396
397                        auto split_node = rt_info[rt_key].as<std::shared_ptr<ov::op::v1::Split>>();
398                        return split_node->output(static_cast<size_t>(split_index));
399                    }
400                }
401            }
402        }
403    }
404
405    // Lambda function to process a single view operation
406    auto process_single_view =
407        [](ov::Output<ov::Node> current, size_t view_offset, const std::vector<size_t> & view_stride,
408           const ov::Shape & view_ggml_shape, const ov::PartialShape & view_ov_shape, const std::string & view_name,
409           size_t view_src_offset, const std::vector<size_t> & view_src_stride, const ov::Shape & view_src_ggml_shape,
410           const ov::PartialShape & view_src_ov_shape, const std::string & view_src_name) -> ov::Output<ov::Node> {
411        auto build_reshape_pattern = [](const ov::PartialShape & target_ov_shape,
412                                        const ov::Shape & target_ggml_shape) -> std::vector<int64_t> {
413            const size_t ndims = target_ggml_shape.size();
414            std::vector<int64_t> reshape_pattern(ndims);
415            size_t dynamic_dims = 0;
416
417            if (target_ov_shape.rank().is_static() &&
418                target_ov_shape.rank().get_length() == static_cast<int64_t>(ndims)) {
419                for (size_t i = 0; i < ndims; ++i) {
420                    if (target_ov_shape[i].is_static()) {
421                        reshape_pattern[i] = target_ov_shape[i].get_length();
422                    } else {
423                        reshape_pattern[i] = -1;
424                        ++dynamic_dims;
425                    }
426                }
427            } else {
428                dynamic_dims = 2;
429            }
430
431            if (dynamic_dims > 1) {
432                for (size_t i = 0; i < ndims; ++i) {
433                    reshape_pattern[i] = static_cast<int64_t>(target_ggml_shape[i]);
434                }
435            }
436
437            return reshape_pattern;
438        };
439
440        auto build_prefix_tail_reshape_pattern = [](const ov::PartialShape & target_ov_shape,
441                                                    const ov::Shape & target_ggml_shape, size_t prefix_dims,
442                                                    int64_t tail_dim) -> std::vector<int64_t> {
443            std::vector<int64_t> reshape_pattern(prefix_dims + 1);
444            size_t dynamic_dims = 0;
445
446            if (target_ov_shape.rank().is_static() &&
447                target_ov_shape.rank().get_length() == static_cast<int64_t>(target_ggml_shape.size())) {
448                for (size_t i = 0; i < prefix_dims; ++i) {
449                    if (target_ov_shape[i].is_static()) {
450                        reshape_pattern[i] = target_ov_shape[i].get_length();
451                    } else {
452                        reshape_pattern[i] = -1;
453                        ++dynamic_dims;
454                    }
455                }
456            } else {
457                dynamic_dims = 2;
458            }
459
460            if (dynamic_dims > 1) {
461                for (size_t i = 0; i < prefix_dims; ++i) {
462                    reshape_pattern[i] = static_cast<int64_t>(target_ggml_shape[i]);
463                }
464            }
465
466            reshape_pattern[prefix_dims] = tail_dim;
467            return reshape_pattern;
468        };
469
470        bool same_stride = view_stride.size() == view_src_stride.size();
471        if (same_stride) {
472            for (size_t i = 0; i < view_stride.size(); ++i) {
473                if (view_stride[i] != view_src_stride[i]) {
474                    same_stride = false;
475                    break;
476                }
477            }
478        }
479
480        bool same_ggml_shape = view_ggml_shape.size() == view_src_ggml_shape.size();
481        if (same_ggml_shape) {
482            for (size_t i = 0; i < view_ggml_shape.size(); ++i) {
483                if (view_ggml_shape[i] != view_src_ggml_shape[i]) {
484                    same_ggml_shape = false;
485                    break;
486                }
487            }
488        }
489
490        if (same_stride && same_ggml_shape) {
491            return current;
492        }
493
494        if (same_stride) {
495            const size_t relative_offset = view_offset >= view_src_offset ? view_offset - view_src_offset : 0;
496            const size_t ndims = view_stride.size();
497
498            std::vector<int> diff_dims;
499            if (view_ggml_shape.size() == ndims && view_src_ggml_shape.size() == ndims) {
500                for (size_t i = 0; i < ndims; ++i) {
501                    if (view_ggml_shape[i] != view_src_ggml_shape[i]) {
502                        diff_dims.push_back(static_cast<int>(i));
503                    }
504                }
505            }
506
507            if (diff_dims.size() == 1) {
508                const int slice_dim = diff_dims[0];
509                const int64_t dim_size = static_cast<int64_t>(view_src_ggml_shape[slice_dim]);
510
511                if (view_stride[slice_dim] > 0 && relative_offset % view_stride[slice_dim] == 0) {
512                    const int64_t begin_val = static_cast<int64_t>((relative_offset / view_stride[slice_dim]) %
513                                                                   static_cast<size_t>(dim_size));
514                    const int64_t end_val = begin_val + static_cast<int64_t>(view_ggml_shape[slice_dim]);
515
516                    if (begin_val >= 0 && end_val <= dim_size) {
517                        auto sliced = std::make_shared<ov::op::v8::Slice>(
518                            current, ov::op::v0::Constant::create(ov::element::i64, {1}, {begin_val}),
519                            ov::op::v0::Constant::create(ov::element::i64, {1}, {end_val}),
520                            ov::op::v0::Constant::create(ov::element::i64, {1}, {1}),
521                            ov::op::v0::Constant::create(ov::element::i64, {1}, {slice_dim}));
522
523                        if (view_ov_shape.is_static()) {
524                            auto reshaped = std::make_shared<ov::op::v1::Reshape>(
525                                sliced,
526                                ov::op::v0::Constant::create(ov::element::i64, {ndims}, view_ov_shape.to_shape()),
527                                false);
528                            reshaped->set_friendly_name(view_name);
529                            return reshaped;
530                        }
531
532                        sliced->set_friendly_name(view_name);
533                        return sliced;
534                    }
535                }
536
537                int64_t tail_src_elems = 1;
538                int64_t tail_dst_elems = 1;
539                for (size_t i = slice_dim; i < ndims; ++i) {
540                    tail_src_elems *= static_cast<int64_t>(view_src_ggml_shape[i]);
541                    tail_dst_elems *= static_cast<int64_t>(view_ggml_shape[i]);
542                }
543
544                const size_t elem_stride = view_stride[ndims - 1];
545                int64_t tail_begin = 0;
546                if (elem_stride > 0) {
547                    tail_begin =
548                        static_cast<int64_t>((relative_offset / elem_stride) % static_cast<size_t>(tail_src_elems));
549                }
550                const int64_t tail_end = tail_begin + tail_dst_elems;
551
552                if (tail_begin >= 0 && tail_end <= tail_src_elems) {
553                    std::vector<int64_t> flat_shape;
554                    for (int i = 0; i < slice_dim; ++i) {
555                        flat_shape.push_back(static_cast<int64_t>(view_src_ggml_shape[i]));
556                    }
557                    flat_shape.push_back(tail_src_elems);
558                    const size_t flat_ndims = flat_shape.size();
559
560                    auto flat = std::make_shared<ov::op::v1::Reshape>(
561                        current, ov::op::v0::Constant::create(ov::element::i64, {flat_ndims}, flat_shape), false);
562
563                    auto sliced = std::make_shared<ov::op::v8::Slice>(
564                        flat, ov::op::v0::Constant::create(ov::element::i64, {1}, {tail_begin}),
565                        ov::op::v0::Constant::create(ov::element::i64, {1}, {tail_end}),
566                        ov::op::v0::Constant::create(ov::element::i64, {1}, {1}),
567                        ov::op::v0::Constant::create(ov::element::i64, {1}, {slice_dim}));
568
569                    if (view_ov_shape.is_static()) {
570                        auto reshaped = std::make_shared<ov::op::v1::Reshape>(
571                            sliced, ov::op::v0::Constant::create(ov::element::i64, {ndims}, view_ov_shape.to_shape()),
572                            false);
573                        reshaped->set_friendly_name(view_name);
574                        return reshaped;
575                    }
576
577                    sliced->set_friendly_name(view_name);
578                    return sliced;
579                }
580            }
581
582            std::vector<int64_t> begin(ndims, 0);
583            std::vector<int64_t> end(ndims, 0);
584            std::vector<int64_t> step(ndims, 1);
585            std::vector<int64_t> axes(ndims, 0);
586
587            size_t remaining_offset = relative_offset;
588            for (size_t i = 0; i < ndims; ++i) {
589                axes[i] = static_cast<int64_t>(i);
590                if (view_stride[i] > 0) {
591                    begin[i] = static_cast<int64_t>(remaining_offset / view_stride[i]);
592                    remaining_offset %= view_stride[i];
593                }
594                end[i] = begin[i] + static_cast<int64_t>(view_ggml_shape[i]);
595            }
596
597            bool in_bounds = view_src_ggml_shape.size() == ndims && view_ggml_shape.size() == ndims;
598            if (in_bounds) {
599                for (size_t i = 0; i < ndims; ++i) {
600                    if (end[i] > static_cast<int64_t>(view_src_ggml_shape[i])) {
601                        in_bounds = false;
602                        break;
603                    }
604                }
605            }
606
607            if (in_bounds && remaining_offset == 0) {
608                auto sliced = std::make_shared<ov::op::v8::Slice>(
609                    current, ov::op::v0::Constant::create(ov::element::i64, {ndims}, begin),
610                    ov::op::v0::Constant::create(ov::element::i64, {ndims}, end),
611                    ov::op::v0::Constant::create(ov::element::i64, {ndims}, step),
612                    ov::op::v0::Constant::create(ov::element::i64, {ndims}, axes));
613
614                sliced->set_friendly_name(view_name);
615                return sliced;
616            }
617        } else {
618            bool same_rank = view_stride.size() == view_src_stride.size() &&
619                             view_ggml_shape.size() == view_src_ggml_shape.size() &&
620                             view_stride.size() == view_ggml_shape.size();
621            const size_t relative_offset = view_offset >= view_src_offset ? view_offset - view_src_offset : 0;
622
623            if (same_rank) {
624                const size_t ndims = view_ggml_shape.size();
625                std::vector<int> diff_dims;
626                for (size_t i = 0; i < ndims; ++i) {
627                    if (view_ggml_shape[i] != view_src_ggml_shape[i]) {
628                        diff_dims.push_back(static_cast<int>(i));
629                    }
630                }
631
632                if (diff_dims.size() == 1) {
633                    const size_t slice_dim = static_cast<size_t>(diff_dims[0]);
634                    bool suffix_stride_match = true;
635                    for (size_t i = slice_dim + 1; i < ndims; ++i) {
636                        if (view_stride[i] != view_src_stride[i]) {
637                            suffix_stride_match = false;
638                            break;
639                        }
640                    }
641
642                    if (suffix_stride_match && view_src_stride[slice_dim] > 0 &&
643                        relative_offset % view_src_stride[slice_dim] == 0) {
644                        const int64_t begin_val = static_cast<int64_t>(relative_offset / view_src_stride[slice_dim]);
645                        const int64_t end_val = begin_val + static_cast<int64_t>(view_ggml_shape[slice_dim]);
646                        const int64_t dim_size = static_cast<int64_t>(view_src_ggml_shape[slice_dim]);
647
648                        if (begin_val >= 0 && end_val <= dim_size) {
649                            auto sliced = std::make_shared<ov::op::v8::Slice>(
650                                current, ov::op::v0::Constant::create(ov::element::i64, {1}, {begin_val}),
651                                ov::op::v0::Constant::create(ov::element::i64, {1}, {end_val}),
652                                ov::op::v0::Constant::create(ov::element::i64, {1}, {1}),
653                                ov::op::v0::Constant::create(ov::element::i64, {1}, {static_cast<int64_t>(slice_dim)}));
654                            sliced->set_friendly_name(view_name);
655                            return sliced;
656                        }
657                    }
658                }
659            }
660
661            size_t view_elems = 1;
662            size_t src_elems = 1;
663            if (same_rank) {
664                for (size_t i = 0; i < view_ggml_shape.size(); ++i) {
665                    view_elems *= view_ggml_shape[i];
666                    src_elems *= view_src_ggml_shape[i];
667                }
668            }
669
670            bool same_num_elements = same_rank && view_elems == src_elems;
671
672            if (same_rank && relative_offset == 0 && same_num_elements) {
673                auto reshape_pattern = build_reshape_pattern(view_ov_shape, view_ggml_shape);
674
675                auto reshaped = std::make_shared<ov::op::v1::Reshape>(
676                    current, ov::op::v0::Constant::create(ov::element::i64, {reshape_pattern.size()}, reshape_pattern),
677                    false);
678                reshaped->set_friendly_name(view_name);
679                return reshaped;
680            }
681
682            if (same_rank) {
683                const size_t ndims = view_ggml_shape.size();
684
685                // Match views that can be expressed as a regular strided slice over the
686                // already reconstructed source tensor, e.g. offset on one axis plus step > 1
687                // on another axis.
688                bool is_regular_slice = view_src_ggml_shape.size() == ndims;
689                std::vector<int64_t> begin(ndims, 0);
690                std::vector<int64_t> end(ndims, 0);
691                std::vector<int64_t> step(ndims, 1);
692                std::vector<int64_t> axes(ndims, 0);
693                size_t remaining_offset = relative_offset;
694
695                if (is_regular_slice) {
696                    for (size_t i = 0; i < ndims; ++i) {
697                        axes[i] = static_cast<int64_t>(i);
698
699                        if (view_src_stride[i] == 0 || view_stride[i] == 0 ||
700                            view_stride[i] % view_src_stride[i] != 0) {
701                            is_regular_slice = false;
702                            break;
703                        }
704
705                        step[i] = static_cast<int64_t>(view_stride[i] / view_src_stride[i]);
706                        if (step[i] <= 0) {
707                            is_regular_slice = false;
708                            break;
709                        }
710
711                        begin[i] = static_cast<int64_t>(remaining_offset / view_src_stride[i]);
712                        remaining_offset %= view_src_stride[i];
713
714                        if (view_ggml_shape[i] == 0) {
715                            end[i] = begin[i];
716                            continue;
717                        }
718
719                        end[i] = begin[i] + step[i] * static_cast<int64_t>(view_ggml_shape[i] - 1) + 1;
720
721                        if (begin[i] < 0 || end[i] > static_cast<int64_t>(view_src_ggml_shape[i])) {
722                            is_regular_slice = false;
723                            break;
724                        }
725                    }
726                }
727
728                if (is_regular_slice && remaining_offset == 0) {
729                    auto sliced = std::make_shared<ov::op::v8::Slice>(
730                        current, ov::op::v0::Constant::create(ov::element::i64, {ndims}, begin),
731                        ov::op::v0::Constant::create(ov::element::i64, {ndims}, end),
732                        ov::op::v0::Constant::create(ov::element::i64, {ndims}, step),
733                        ov::op::v0::Constant::create(ov::element::i64, {ndims}, axes));
734
735                    sliced->set_friendly_name(view_name);
736                    return sliced;
737                }
738
739                const size_t elem_stride = view_src_stride.back();
740                const bool aligned_offset = elem_stride > 0 && relative_offset % elem_stride == 0;
741
742                if (aligned_offset) {
743                    size_t suffix_start = 0;
744                    size_t expected_stride = elem_stride;
745                    for (int i = static_cast<int>(ndims) - 1; i >= 0; --i) {
746                        if (view_stride[i] != expected_stride) {
747                            suffix_start = static_cast<size_t>(i + 1);
748                            break;
749                        }
750                        expected_stride *= view_ggml_shape[i];
751                    }
752
753                    size_t prefix_elems = 1;
754                    size_t suffix_elems = 1;
755                    for (size_t i = 0; i < suffix_start; ++i) {
756                        prefix_elems *= view_ggml_shape[i];
757                    }
758                    for (size_t i = suffix_start; i < ndims; ++i) {
759                        suffix_elems *= view_ggml_shape[i];
760                    }
761
762                    if (prefix_elems > 0 && src_elems % prefix_elems == 0) {
763                        const size_t src_tail_elems = src_elems / prefix_elems;
764                        const int64_t tail_begin = static_cast<int64_t>(relative_offset / elem_stride);
765                        const int64_t tail_end = tail_begin + static_cast<int64_t>(suffix_elems);
766
767                        if (tail_begin >= 0 && tail_end <= static_cast<int64_t>(src_tail_elems)) {
768                            auto prefix_tail_pattern = build_prefix_tail_reshape_pattern(
769                                view_ov_shape, view_ggml_shape, suffix_start, static_cast<int64_t>(src_tail_elems));
770
771                            auto prefix_tail = std::make_shared<ov::op::v1::Reshape>(
772                                current,
773                                ov::op::v0::Constant::create(ov::element::i64, {prefix_tail_pattern.size()},
774                                                             prefix_tail_pattern),
775                                false);
776
777                            ov::Output<ov::Node> selected = prefix_tail;
778                            if (tail_begin != 0 || tail_end != static_cast<int64_t>(src_tail_elems)) {
779                                selected = std::make_shared<ov::op::v8::Slice>(
780                                    prefix_tail, ov::op::v0::Constant::create(ov::element::i64, {1}, {tail_begin}),
781                                    ov::op::v0::Constant::create(ov::element::i64, {1}, {tail_end}),
782                                    ov::op::v0::Constant::create(ov::element::i64, {1}, {1}),
783                                    ov::op::v0::Constant::create(ov::element::i64, {1},
784                                                                 {static_cast<int64_t>(suffix_start)}));
785                            }
786
787                            auto reshape_pattern = build_reshape_pattern(view_ov_shape, view_ggml_shape);
788                            auto reshaped = std::make_shared<ov::op::v1::Reshape>(
789                                selected,
790                                ov::op::v0::Constant::create(ov::element::i64, {reshape_pattern.size()},
791                                                             reshape_pattern),
792                                false);
793                            reshaped->set_friendly_name(view_name);
794                            return reshaped;
795                        }
796                    }
797                }
798            }
799
800            return current;
801        }
802
803        (void) view_name;
804        (void) view_src_ov_shape;
805        (void) view_src_name;
806
807        return current;
808    };
809
810    // Special case: ggml collapses VIEW-of-VIEW chains so that `view_offs` is always an
811    // ABSOLUTE offset from the true root allocation, regardless of how many VIEW levels
812    // are in between (see ggml_new_tensor_impl). `src[0]` is still the immediate op-graph
813    // parent though, which can be a DIFFERENT (already narrowed) VIEW with the SAME ggml
814    // shape as this one but a different absolute offset -- e.g. a per-layer deepstack
815    // slice `view_2d(embd, n_embd, n_tokens, embd->nb[1], layer*n_embd*sizeof(float))`
816    // whose src[0] ("embd") is itself already a zero-offset VIEW of the true root (the
817    // padded embedding). Chaining through "embd" here would try to re-slice an already
818    // 2-narrowed tensor using a root-relative offset, going out of bounds and silently
819    // falling back to a no-op (returning the wrong, already-resolved sibling slice).
820    // Detect this (same shape as the immediate src, but different absolute offset) and
821    // re-slice directly from the untouched root using the innermost view's absolute
822    // offset against the ROOT's own shape/stride instead of chaining through src[0].
823    {
824        auto innermost_offset = context.get_view_input_offset(input_index, 0);
825        auto innermost_src_offset = context.get_view_input_src_offset(input_index, 0);
826        auto innermost_shape = context.get_view_input_ggml_shape(input_index, 0);
827        auto innermost_src_shape = context.get_view_input_src_ggml_shape(input_index, 0);
828        if (innermost_offset != innermost_src_offset && innermost_shape == innermost_src_shape) {
829            size_t root_view_idx = view_input_size - 1;
830            auto root_ggml_shape = context.get_view_input_src_ggml_shape(input_index, root_view_idx);
831            auto root_stride = context.get_view_input_src_stride(input_index, root_view_idx);
832            auto root_offset = context.get_view_input_src_offset(input_index, root_view_idx);
833            auto root_ov_shape = context.get_view_input_src_ov_shape(input_index, root_view_idx);
834            auto root_name = context.get_view_input_src_name(input_index, root_view_idx);
835            auto innermost_stride = context.get_view_input_stride(input_index, 0);
836            auto innermost_ov_shape = context.get_view_input_ov_shape(input_index, 0);
837            auto innermost_name = context.get_view_input_name(input_index, 0);
838
839            return process_single_view(input, innermost_offset, innermost_stride, innermost_shape, innermost_ov_shape,
840                                       innermost_name, root_offset, root_stride, root_ggml_shape, root_ov_shape,
841                                       root_name);
842        }
843    }
844
845    // Process views from the base tensor (last) to the current view (first)
846    // Start with the base tensor
847    ov::Output<ov::Node> current = input;
848
849    // Process each view in reverse order (from base to current)
850    for (int view_idx = view_input_size - 1; view_idx >= 0; view_idx--) {
851        auto view_offset = context.get_view_input_offset(input_index, view_idx);
852        auto view_stride = context.get_view_input_stride(input_index, view_idx);
853        auto view_ggml_shape = context.get_view_input_ggml_shape(input_index, view_idx);
854        auto view_ov_shape = context.get_view_input_ov_shape(input_index, view_idx);
855        auto view_name = context.get_view_input_name(input_index, view_idx);
856
857        // print view info
858        // std::cout << "View " << view_idx << ": name = " << view_name << ", offset = " << view_offset << ", stride = ["
859        //       << view_stride[0] << "," << view_stride[1] << "," << view_stride[2] << "," << view_stride[3]
860        //       << "], ggml shape = [" << view_ggml_shape[0] << "," << view_ggml_shape[1] << ","
861        //       << view_ggml_shape[2] << "," << view_ggml_shape[3] << "], ov shape = " << view_ov_shape << std::endl;
862
863        auto view_src_offset = context.get_view_input_src_offset(input_index, view_idx);
864        auto view_src_stride = context.get_view_input_src_stride(input_index, view_idx);
865        auto view_src_ggml_shape = context.get_view_input_src_ggml_shape(input_index, view_idx);
866        auto view_src_ov_shape = context.get_view_input_src_ov_shape(input_index, view_idx);
867        auto view_src_name = context.get_view_input_src_name(input_index, view_idx);
868        // print source view info
869        // std::cout << "View " << view_idx << ": source name = " << view_src_name
870        //           << ", source offset = " << view_src_offset << ", source stride = [" << view_src_stride[0] << ","
871        //           << view_src_stride[1] << "," << view_src_stride[2] << "," << view_src_stride[3]
872        //           << "], source ggml shape = [" << view_src_ggml_shape[0] << "," << view_src_ggml_shape[1] << ","
873        //           << view_src_ggml_shape[2] << "," << view_src_ggml_shape[3]
874        //           << "], source ov shape = " << view_src_ov_shape << std::endl;
875
876        current = process_single_view(current, view_offset, view_stride, view_ggml_shape, view_ov_shape, view_name,
877                                      view_src_offset, view_src_stride, view_src_ggml_shape, view_src_ov_shape,
878                                      view_src_name);
879    }
880
881    return current;
882}
883
884}  // namespace ggml
885}  // namespace frontend
886}  // namespace ov