| 137 | if (rank == 0) { |
| 138 | throw std::runtime_error("Reduce input rank must be positive"); |
| 139 | } |
| 140 | const int normalized = axis < 0 ? static_cast<int>(rank) + axis : axis; |
| 141 | if (normalized < 0 || normalized >= static_cast<int>(rank)) { |
| 142 | throw std::runtime_error("Reduce axis out of range"); |
| 143 | } |
| 144 | return normalized; |
| 145 | } |
| 146 | |
| 147 | core::TensorShape reduced_shape(const core::TensorShape & shape, int axis) { |
| 148 | core::TensorShape out = shape; |
| 149 | out.dims[static_cast<size_t>(axis)] = 1; |
| 150 | return out; |
| 151 | } |
| 152 | |
| 153 | template <typename Fn> |
| 154 | core::TensorValue reduce_axis( |
| 155 | core::ModuleBuildContext & ctx, |
| 156 | const core::TensorValue & input, |
| 157 | const ReduceConfig & config, |
| 158 | Fn fn) { |
| 159 | if (ctx.ggml == nullptr) { |
| 160 | throw std::runtime_error("ModuleBuildContext.ggml is null"); |
| 161 | } |
| 162 | core::validate_rank_between(input, 1, core::kMaxTensorRank, "input"); |
| 163 | const int axis = normalize_reduce_axis(config.axis, input.shape.rank); |
| 164 | if (axis == static_cast<int>(input.shape.rank - 1)) { |
| 165 | const auto contiguous = tensor_layout::ensure_contiguous_layout_if_needed(ctx, input); |
| 166 | return core::wrap_tensor(fn(ctx.ggml, contiguous.tensor), reduced_shape(input.shape, axis), GGML_TYPE_F32); |
no test coverage detected