| 169 | std::array<int, core::kMaxTensorRank> axes = {0, 1, 2, 3}; |
| 170 | std::swap(axes[static_cast<size_t>(axis)], axes[input.shape.rank - 1]); |
| 171 | const auto transposed = TransposeModule({axes, input.shape.rank}).build(ctx, input); |
| 172 | const auto contiguous = tensor_layout::ensure_contiguous_layout_if_needed(ctx, transposed); |
| 173 | auto reduced = core::wrap_tensor( |
| 174 | fn(ctx.ggml, contiguous.tensor), |
| 175 | reduced_shape(transposed.shape, static_cast<int>(transposed.shape.rank - 1)), |
| 176 | GGML_TYPE_F32); |
| 177 | return TransposeModule({axes, reduced.shape.rank}).build(ctx, reduced); |
| 178 | } |
| 179 | |
| 180 | } // namespace |
| 181 | |
| 182 | const core::ModuleSchema & MatMulModule::schema() const noexcept { |
| 183 | return static_schema(); |
| 184 | } |
| 185 | |
| 186 | core::TensorValue MatMulModule::build( |
| 187 | core::ModuleBuildContext & ctx, |
| 188 | const core::TensorValue & lhs, |
| 189 | const core::TensorValue & rhs) const { |
| 190 | if (ctx.ggml == nullptr) { |
| 191 | throw std::runtime_error("ModuleBuildContext.ggml is null"); |
| 192 | } |
| 193 | core::validate_rank_between(lhs, 2, core::kMaxTensorRank, "lhs"); |
| 194 | core::validate_rank_between(rhs, lhs.shape.rank, lhs.shape.rank, "rhs"); |
| 195 | |
| 196 | const size_t rank = lhs.shape.rank; |
| 197 | for (size_t i = 0; i + 2 < rank; ++i) { |
| 198 | if (lhs.shape.dims[i] != rhs.shape.dims[i]) { |
| 199 | throw std::runtime_error("MatMul batch dimensions must match"); |
| 200 | } |
no test coverage detected