char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
511f9c137
char/aliceai-t5-moe
1#include "../op_table.h" 2#include "../utils.h" 3 4#include <openvino/op/concat.hpp> 5#include <openvino/op/constant.hpp> 6#include <openvino/op/gather.hpp> 7#include <openvino/op/reshape.hpp> 8#include <openvino/op/shape_of.hpp> 9#include <openvino/op/slice.hpp> 10#include <set> 11 12namespace ov { 13namespace frontend { 14namespace ggml { 15namespace op { 16 17OutputVector translate_view (const NodeContext & context ) { 18num_inputs_check (context ,1 ,1 ); 19 20if (context .get_op_case ()== 1 ) { 21// Static-mode identity pass-through for VIEWs over a GATED_DELTA_NET combined output or 22// the conv_input CONCAT; the consuming op (CPY/RMS_NORM) does its own runtime-correct 23// slicing on the full tensor (see ggml-decoder.cpp compute_op_case, GGML_OP_VIEW). 24return {context .get_input (0 )}; 25 } 26 27if (!context .is_static ()) { 28// On the stateless/non-static path VIEW is normally a no-op (consumers re-slice). 29// EXCEPTION: the MoE expert aggregation slices each expert plane out of 30// ffn_moe_weighted [n_embd, n_expert_used, n_tokens] with ggml_view_2d and then 31// sums the planes with a chain of ADDs (llama-graph.cpp). Those ADDs read this 32// VIEW node directly from the tensor map and do NOT re-slice, so a no-op here 33// makes every plane the full tensor and the expert sum collapses. Materialize the 34// single-expert slice here. Gated by name (ffn_moe_weighted...view) so it can't 35// affect any other view. 36const std::string & vname = context .get_name (); 37if (vname .find ("ffn_moe_weighted" )!= std::string::npos ) { 38auto src_ps = context .get_input_shape (0 ); 39auto dst_ps = context .get_output_shape (); 40if (src_ps .rank ().is_static ()&& dst_ps .rank ().is_static ()&& src_ps .rank ()== dst_ps .rank ()&& 41src_ps .is_static ()&& dst_ps .is_static ()) { 42auto sst = context .get_input_stride (0 ); 43auto dst = context .get_output_stride (); 44size_t voff = context .get_output_op_offset (); 45auto ss = src_ps .to_shape (); 46auto dd = dst_ps .to_shape (); 47const size_t nd = ss .size (); 48if (sst .size ()== nd && dst .size ()== nd ) { 49// Map each dst axis of size>1 to a src axis with equal (size,stride); 50// the unmatched src axis of size>1 is the indexed expert axis. 51// dst_to_src[d] records which src axis each dst axis came from, so we can 52// later pull the dynamic (token) dim from the right source axis at runtime. 53 std::vector < bool > used (nd ,false ); 54 std::vector < int > dst_to_src (nd ,-1 ); 55bool ok = true; 56for (size_t d = 0 ;d < nd ;++ d ) { 57if (dd [d ]== 1 ) { 58continue ; 59 } 60int found = -1 ; 61for (size_t s = 0 ;s < nd ;++ s ) { 62if (!used [s ]&& ss [s ]== dd [d ]&& sst [s ]== dst [d ]) { 63found = (int )s ; 64break ; 65 } 66 } 67if (found < 0 ) { 68ok = false; 69break ; 70 } 71used [found ]= true; 72dst_to_src [d ]= found ; 73 } 74int dropped = -1 ; 75if (ok ) { 76for (size_t s = 0 ;s < nd ;++ s ) { 77if (!used [s ]&& ss [s ]> 1 ) { 78if (dropped >=0 ) { 79ok = false; 80break ; 81 } 82dropped = (int )s ; 83 } 84 } 85 } 86if (ok && dropped >=0 ) { 87const size_t dstr = sst [dropped ]; 88const int64_t dsz = (int64_t )ss [dropped ]; 89if (dstr > 0 && voff %dstr == 0 ) { 90const int64_t sel = (int64_t ) (voff /dstr ); 91if (sel >=0 && sel < dsz ) { 92 ov::Output < ov::Node > sl = std::make_shared < ov::op::v8::Slice > ( 93context .get_input (0 ), 94 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {sel }), 95 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {sel + 1 }), 96 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {1 }), 97 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {dropped })); 98// Build the reshape target from the (concrete) dst shape, but 99// keep the dynamic token axis dynamic instead of freezing it 100// to the captured n_tokens. Without this the constant dst 101// shape bakes in the prefill token count and the static value 102// flows downstream, turning every later decoder layer static 103// (the GPU in-place-concat KV-cache bug). The token axis is 104// PERMUTED between the sliced input and the dst (e.g. input 105// [1,tok,expert,emb] -> dst [1,1,tok,emb]), so special_zero 106// (which copies the same-position dim) is not enough: pull the 107// dynamic dim from the correct SOURCE axis via ShapeOf+Gather 108// and place it at the dst token position. 109const int32_t dyn = context .get_op_dynamic_dim ();// output ggml axis, -1 if none 110int dst_ov_axis = (dyn != -1 ) ? (3 - (int )dyn ) :-1 ;// get_shape() reverses ggml order 111int src_ov_axis = (dst_ov_axis >=0 && dst_ov_axis < (int )nd ) 112 ?dst_to_src [dst_ov_axis ] 113 :-1 ; 114if (dst_ov_axis >=0 && src_ov_axis >=0 ) { 115// target = concat of per-axis scalars; the token axis is a 116// runtime Gather of the slice's shape, the rest are constants. 117auto sl_shape = std::make_shared < ov::op::v3::ShapeOf > (sl , ov::element::i64 ); 118auto tok_dim = std::make_shared < ov::op::v8::Gather > ( 119sl_shape , 120 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {src_ov_axis }), 121 ov::op::v0::Constant ::create (ov::element::i64 , {}, {0 })); 122 ov::OutputVector parts ; 123for (int a = 0 ;a < (int )nd ;++ a ) { 124if (a == dst_ov_axis ) { 125parts .push_back (tok_dim ); 126 }else { 127parts .push_back (ov::op::v0::Constant ::create ( 128 ov::element::i64 , {1 }, {(int64_t )dd [a ]})); 129 } 130 } 131auto dc = std::make_shared < ov::op::v0::Concat > (parts ,0 ); 132auto rs = std::make_shared < ov::op::v1::Reshape > (sl ,dc , false); 133return rename_outputs_with_suffix ({rs },context .get_name ()); 134 } 135auto dc = ov::op::v0::Constant ::create ( 136 ov::element::i64 , {nd }, std::vector < int64_t > (dd .begin (),dd .end ())); 137auto rs = std::make_shared < ov::op::v1::Reshape > (sl ,dc , false); 138return rename_outputs_with_suffix ({rs },context .get_name ()); 139 } 140 } 141 } 142 } 143 } 144 } 145return {context .get_input (0 )}; 146 } 147 148auto input = context .get_input (0 ); 149auto src_shape = context .get_input_shape (0 ); 150auto dst_shape = context .get_output_shape (); 151 152if (src_shape .rank ().is_dynamic ()|| dst_shape .rank ().is_dynamic ()) { 153return {input }; 154 } 155 156int64_t src_elems = 1 ,dst_elems = 1 ; 157for (int64_t i = 0 ;i < src_shape .rank ().get_length ();++ i ) { 158if (src_shape [i ].is_dynamic ()) { 159return {input }; 160 } 161src_elems *=src_shape [i ].get_length (); 162 } 163for (int64_t i = 0 ;i < dst_shape .rank ().get_length ();++ i ) { 164if (dst_shape [i ].is_dynamic ()) { 165return {input }; 166 } 167dst_elems *=dst_shape [i ].get_length (); 168 } 169 170if (dst_elems >=src_elems ) { 171return {input }; 172 } 173 174auto src_stride = context .get_input_stride (0 ); 175auto dst_stride = context .get_output_stride (); 176size_t view_offset = context .get_output_op_offset (); 177 178bool same_stride = (src_stride .size ()== dst_stride .size ()); 179if (same_stride ) { 180for (size_t i = 0 ;i < src_stride .size ();++ i ) { 181if (src_stride [i ]!= dst_stride [i ]) { 182same_stride = false; 183break ; 184 } 185 } 186 } 187 188if (!same_stride ) { 189return {input }; 190 } 191 192auto src_ov_shape = src_shape .to_shape (); 193auto dst_ov_shape = dst_shape .to_shape (); 194size_t ndims = src_ov_shape .size (); 195if (dst_ov_shape .size ()!= ndims ) { 196return {input }; 197 } 198 199 std::vector < int > diff_dims ; 200for (size_t i = 0 ;i < ndims ;++ i ) { 201if (src_ov_shape [i ]!= dst_ov_shape [i ]) { 202diff_dims .push_back (static_cast < int > (i )); 203 } 204 } 205 206if (diff_dims .size ()!= 1 ) { 207return {input }; 208 } 209 210int slice_dim = diff_dims [0 ]; 211int64_t dim_size = static_cast < int64_t > (src_ov_shape [slice_dim ]); 212 213size_t ov_stride_for_dim = 1 ; 214for (size_t i = slice_dim + 1 ;i < ndims ;++ i ) { 215ov_stride_for_dim *=src_ov_shape [i ]; 216 } 217size_t elem_size = src_stride .back (); 218if (elem_size == 0 ) { 219elem_size = 1 ; 220 } 221 222int64_t begin_val = 0 ; 223if (ov_stride_for_dim > 0 && elem_size > 0 ) { 224begin_val = static_cast < int64_t > ((view_offset /elem_size ) /ov_stride_for_dim ); 225 } 226int64_t end_val = begin_val + static_cast < int64_t > (dst_ov_shape [slice_dim ]); 227 228if (begin_val < 0 || end_val > dim_size ) { 229return {input }; 230 } 231 232auto sliced = 233 std::make_shared < ov::op::v8::Slice > (input , ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {begin_val }), 234 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {end_val }), 235 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {1 }), 236 ov::op::v0::Constant ::create (ov::element::i64 , {1 }, {slice_dim })); 237 238sliced -> set_friendly_name (context .get_output_name ()); 239return {sliced -> output (0 )}; 240} 241 242}// namespace op 243}// namespace ggml 244}// namespace frontend 245}// namespace ov