#include "ggml.h"
#include "mmq.hpp"
#include "quantize.hpp"
#include <algorithm>
#include <future>
#include <vector>
static void ggml_zdnn_mul_mat_q_grouped(ggml_backend_zdnn_context * ctx,
int32_t n_threads,
const ggml_tensor * inputs,
const ggml_backend_zdnn_buffer * weights_extra,
ggml_tensor * output) {
const int64_t ne10 = inputs->ne[0];
const int64_t ne11 = inputs->ne[1];
const int64_t ne0 = output->ne[0];
const int64_t n_groups = weights_extra->pre_tfm_desc.dim3;
const int64_t gs = weights_extra->pre_tfm_desc.dim2;
GGML_ASSERT(n_groups * gs >= ne10 && (n_groups - 1) * gs < ne10);
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * n_groups * gs * ne11);
int8_t * inputs_q = inputs_q_alloc.get();
std::vector<float> inputs_scales(n_groups * ne11);
ggml_zdnn_quantize_inputs(n_threads, inputs, gs, weights_extra->channel_scales.data(),
inputs_q, inputs_q + n_groups * gs * ne11, inputs_scales.data());
zdnn_tensor_desc inputs_pre_tfm_desc;
zdnn_tensor_desc inputs_tfm_desc;
zdnn_ztensor inputs_ztensor;
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2 * n_groups, ne11, gs);
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
zdnn_tensor_desc group_pre_tfm_desc;
zdnn_tensor_desc group_tfm_desc;
zdnn_init_pre_transformed_desc(ZDNN_2D, FP32, &group_pre_tfm_desc, ne11, ne0);
ZDNN_CHECK(zdnn_generate_transformed_desc(&group_pre_tfm_desc, &group_tfm_desc));
const uint64_t group_bytes = zdnn_getsize_ztensor(&group_tfm_desc);
constexpr int64_t chunk_bytes = 8*1024*1024;
const int64_t n_chunk = std::clamp<int64_t>(chunk_bytes / (int64_t)group_bytes, 1, n_groups);
const zdnn_ztensor & bias_ztensor = ggml_zdnn_zero_bias(ctx, n_chunk, ne0);
zdnn_tensor_desc partial_pre_tfm_desc;
zdnn_tensor_desc partial_tfm_desc;
zdnn_ztensor partial_ztensor[2];
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc[2];
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2 * n_chunk, ne11, ne0);
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
GGML_ASSERT(zdnn_getsize_ztensor(&partial_tfm_desc) == (uint64_t)(2 * n_chunk) * group_bytes);
for (int32_t i = 0; i < 2; i++) {
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor[i], partial_ztensor_alloc[i]);
}
const float * weights_scales = weights_extra->scales.data();
ggml_zdnn_pool_alloc<float> partial_alloc(ctx->pool, 2 * ne11 * ne0);
float * partial = partial_alloc.get();
float * partial_res = partial + ne11 * ne0;
const auto sum_chunk = [&](const zdnn_ztensor & chunk, int64_t g0, int64_t n) {
zdnn_ztensor group_ztensor;
zdnn_init_ztensor(&group_pre_tfm_desc, &group_tfm_desc, &group_ztensor);
for (int64_t g = g0; g < g0 + n; g++) {
group_ztensor.buffer_size = group_bytes;
group_ztensor.is_transformed = true;
group_ztensor.buffer = (char *)chunk.buffer + (g - g0)*group_bytes;
ZDNN_CHECK(zdnn_transform_origtensor(&group_ztensor, partial));
group_ztensor.buffer = (char *)chunk.buffer + (n_chunk + g - g0)*group_bytes;
ZDNN_CHECK(zdnn_transform_origtensor(&group_ztensor, partial_res));
const float * sw = weights_scales + g*ne0;
for (int64_t i1 = 0; i1 < ne11; i1++) {
const float sa = inputs_scales[g*ne11 + i1];
const float * p = partial + i1*ne0;
const float * pr = partial_res + i1*ne0;
float * y = (float *)output->data + i1*ne0;
if (g == 0) {
for (int64_t i0 = 0; i0 < ne0; i0++) {
y[i0] = sa * sw[i0] * (p[i0] + pr[i0] * (1.0f/254.0f));
}
} else {
for (int64_t i0 = 0; i0 < ne0; i0++) {
y[i0] += sa * sw[i0] * (p[i0] + pr[i0] * (1.0f/254.0f));
}
}
}
}
};
zdnn_tensor_desc view_pre_tfm_desc[6];
zdnn_tensor_desc view_tfm_desc[6];
std::future<void> sum_task;
for (int64_t c = 0, g0 = 0; g0 < n_groups; c++, g0 += n_chunk) {
const int64_t n = std::min(n_chunk, n_groups - g0);
const zdnn_ztensor inputs_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[0], &view_tfm_desc[0], inputs_ztensor, 2 * n_groups, g0, n);
const zdnn_ztensor inputs_res_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[1], &view_tfm_desc[1], inputs_ztensor, 2 * n_groups, n_groups + g0, n);
const zdnn_ztensor weights_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[2], &view_tfm_desc[2], weights_extra->ztensor, n_groups, g0, n);
const zdnn_ztensor bias_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[3], &view_tfm_desc[3], bias_ztensor, n_chunk, 0, n);
zdnn_ztensor & chunk = partial_ztensor[c % 2];
chunk.pre_transformed_desc = &partial_pre_tfm_desc;
chunk.transformed_desc = &partial_tfm_desc;
chunk.is_transformed = false;
zdnn_ztensor chunk_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[4], &view_tfm_desc[4], chunk, 2 * n_chunk, 0, n);
zdnn_ztensor chunk_res_view = ggml_zdnn_stack_view(&view_pre_tfm_desc[5], &view_tfm_desc[5], chunk, 2 * n_chunk, n_chunk, n);
ZDNN_CHECK(zdnn_quantized_matmul_op(&inputs_view, &weights_view, &bias_view,
MATMUL_OP_ADDITION, -127, 127, true, false, true,
nullptr, &chunk_view));
ZDNN_CHECK(zdnn_quantized_matmul_op(&inputs_res_view, &weights_view, &bias_view,
MATMUL_OP_ADDITION, -127, 127, true, false, true,
nullptr, &chunk_res_view));
if (sum_task.valid()) {
sum_task.get();
}
if (n_threads > 1) {
sum_task = std::async(std::launch::async, sum_chunk, chunk, g0, n);
} else {
sum_chunk(chunk, g0, n);
}
}
if (sum_task.valid()) {
sum_task.get();
}
}
void ggml_zdnn_mul_mat_q(
ggml_backend_zdnn_context * ctx,
const ggml_tensor * src0,
const ggml_tensor * src1,
ggml_tensor * dst) {
GGML_TENSOR_BINARY_OP_LOCALS;
const enum ggml_type type = src0->type;
GGML_ASSERT(ne0 == ne01);
GGML_ASSERT(ne1 == ne11);
GGML_ASSERT(ne2 == ne12);
GGML_ASSERT(ne3 == ne13);
GGML_ASSERT(nb00 == ggml_type_size(type));
GGML_ASSERT(nb10 == ggml_type_size(src1->type));
GGML_ASSERT(nb0 == sizeof(float));
GGML_ASSERT(nb0 <= nb1);
GGML_ASSERT(nb1 <= nb2);
GGML_ASSERT(nb2 <= nb3);
const ggml_tensor * weights = src0;
const ggml_tensor * inputs = src1;
ggml_tensor * output = dst;
GGML_ASSERT(inputs->type == GGML_TYPE_F32);
ggml_backend_zdnn_buffer * weights_extra = (ggml_backend_zdnn_buffer *)weights->extra;
ggml_backend_zdnn_buffer * output_extra = (ggml_backend_zdnn_buffer *)output->extra;
const int32_t n_threads = ne10*ne11 >= 65536 ? ctx->n_threads : 1;
if (weights_extra->pre_tfm_desc.layout == ZDNN_3DS) {
GGML_ASSERT(weights_extra->pre_tfm_desc.dim1 == weights->ne[1] && "weights_extra->pre_tfm_desc.dim1 must match weights->ne[1]");
ggml_zdnn_mul_mat_q_grouped(ctx, n_threads, inputs, weights_extra, output);
output_extra->ztensor.is_transformed = false;
return;
}
ggml_backend_zdnn_buffer * bias_extra = (ggml_backend_zdnn_buffer *)output_extra->extra;
GGML_ASSERT(weights_extra->pre_tfm_desc.dim2 == weights->ne[0] && "weights_extra->pre_tfm_desc.dim2 must match weights->ne[0]");
GGML_ASSERT(weights_extra->pre_tfm_desc.dim1 == weights->ne[1] && "weights_extra->pre_tfm_desc.dim1 must match weights->ne[1]");
ggml_zdnn_pool_alloc<int8_t> inputs_q_alloc(ctx->pool, 2 * ne10 * ne11);
int8_t * inputs_q = inputs_q_alloc.get();
std::vector<float> inputs_scales(ne11);
ggml_zdnn_quantize_inputs(n_threads, inputs, ne10, weights_extra->channel_scales.data(),
inputs_q, inputs_q + ne10 * ne11, inputs_scales.data());
zdnn_tensor_desc inputs_pre_tfm_desc;
zdnn_tensor_desc inputs_tfm_desc;
zdnn_ztensor inputs_ztensor;
ggml_zdnn_pool_alloc<uint8_t> inputs_ztensor_alloc;
zdnn_init_pre_transformed_desc(ZDNN_3DS, INT8, &inputs_pre_tfm_desc, 2, ne11, ne10);
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&inputs_pre_tfm_desc, QUANTIZED_INT8, &inputs_tfm_desc));
ggml_zdnn_init_scratch_ztensor(ctx, &inputs_pre_tfm_desc, &inputs_tfm_desc, &inputs_ztensor, inputs_ztensor_alloc);
ZDNN_CHECK(zdnn_transform_quantized_ztensor(&inputs_ztensor, false, -127, 127, inputs_q));
zdnn_tensor_desc partial_pre_tfm_desc;
zdnn_tensor_desc partial_tfm_desc;
zdnn_ztensor partial_ztensor;
ggml_zdnn_pool_alloc<uint8_t> partial_ztensor_alloc;
zdnn_init_pre_transformed_desc(ZDNN_3DS, FP32, &partial_pre_tfm_desc, 2, ne11, ne0);
ZDNN_CHECK(zdnn_generate_quantized_transformed_desc(&partial_pre_tfm_desc, QUANTIZED_DLFLOAT16, &partial_tfm_desc));
ggml_zdnn_init_scratch_ztensor(ctx, &partial_pre_tfm_desc, &partial_tfm_desc, &partial_ztensor, partial_ztensor_alloc);
ZDNN_CHECK(zdnn_quantized_matmul_op(&inputs_ztensor, &weights_extra->ztensor, &bias_extra->ztensor,
MATMUL_OP_ADDITION, -127, 127, true, false, true,
nullptr, &partial_ztensor));
zdnn_tensor_desc stack_pre_tfm_desc;
zdnn_tensor_desc stack_tfm_desc;
zdnn_init_pre_transformed_desc(ZDNN_2D, FP32, &stack_pre_tfm_desc, ne11, ne0);
ZDNN_CHECK(zdnn_generate_transformed_desc(&stack_pre_tfm_desc, &stack_tfm_desc));
const uint64_t stack_bytes = zdnn_getsize_ztensor(&stack_tfm_desc);
GGML_ASSERT(partial_ztensor.buffer_size == 2*stack_bytes);
ggml_zdnn_pool_alloc<float> partial_res_alloc(ctx->pool, ne11 * ne0);
float * partial[2] = { (float *)output->data, partial_res_alloc.get() };
ggml_zdnn_parallel_for(n_threads, 2, [&](int64_t s_start, int64_t s_end) {
zdnn_ztensor stack_ztensor;
zdnn_init_ztensor(&stack_pre_tfm_desc, &stack_tfm_desc, &stack_ztensor);
for (int64_t s = s_start; s < s_end; s++) {
stack_ztensor.buffer = (char *)partial_ztensor.buffer + s*stack_bytes;
stack_ztensor.buffer_size = stack_bytes;
stack_ztensor.is_transformed = true;
ZDNN_CHECK(zdnn_transform_origtensor(&stack_ztensor, partial[s]));
}
});
output_extra->ztensor.is_transformed = false;
const float * weights_scales = weights_extra->scales.data();
ggml_zdnn_parallel_for(n_threads, ne1, [&](int64_t i1_start, int64_t i1_end) {
for (int64_t i1 = i1_start; i1 < i1_end; i1++) {
const float sa = inputs_scales[i1];
const float * pr = partial[1] + i1*ne0;
float * y = partial[0] + i1*ne0;
for (int64_t i0 = 0; i0 < ne0; i0++) {
y[i0] = sa * weights_scales[i0] * (y[i0] + pr[i0] * (1.0f/254.0f));
}
}
});
}