-
Notifications
You must be signed in to change notification settings - Fork 20.7k
ggml: add q1_0_g128 quantization type #21766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -427,7 +427,8 @@ extern "C" { | |||||||
| // GGML_TYPE_IQ4_NL_4_8 = 37, | ||||||||
| // GGML_TYPE_IQ4_NL_8_8 = 38, | ||||||||
| GGML_TYPE_MXFP4 = 39, // MXFP4 (1 block) | ||||||||
| GGML_TYPE_COUNT = 40, | ||||||||
| GGML_TYPE_Q1_0_g128 = 41, | ||||||||
| GGML_TYPE_COUNT = 42, | ||||||||
| }; | ||||||||
|
|
||||||||
| // precision | ||||||||
|
|
@@ -463,6 +464,7 @@ extern "C" { | |||||||
| GGML_FTYPE_MOSTLY_IQ1_M = 23, // except 1d tensors | ||||||||
| GGML_FTYPE_MOSTLY_BF16 = 24, // except 1d tensors | ||||||||
| GGML_FTYPE_MOSTLY_MXFP4 = 25, // except 1d tensors | ||||||||
|
||||||||
| GGML_FTYPE_MOSTLY_MXFP4 = 25, // except 1d tensors | |
| GGML_FTYPE_MOSTLY_MXFP4 = 25, // except 1d tensors | |
| GGML_FTYPE_RESERVED_26 = 26, // reserved to keep persisted ftype ids stable |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -236,6 +236,13 @@ typedef struct { | |||||||||||||||||||||||||
| } block_q8_1; | ||||||||||||||||||||||||||
| static_assert(sizeof(block_q8_1) == 2*sizeof(ggml_half) + QK8_1, "wrong q8_1 block size/padding"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #define QK1_0_g128 128 | ||||||||||||||||||||||||||
| typedef struct { | ||||||||||||||||||||||||||
| ggml_half d; // shared scale | ||||||||||||||||||||||||||
| uint8_t qs[QK1_0_g128/8]; // sign bits | ||||||||||||||||||||||||||
| } block_q1_0_g128; | ||||||||||||||||||||||||||
| static_assert(sizeof(block_q1_0_g128) == sizeof(ggml_half) + QK1_0_g128/8, "wrong q1_0_g128 block size/padding"); | ||||||||||||||||||||||||||
|
Comment on lines
+239
to
+244
|
||||||||||||||||||||||||||
| #define QK1_0_g128 128 | |
| typedef struct { | |
| ggml_half d; // shared scale | |
| uint8_t qs[QK1_0_g128/8]; // sign bits | |
| } block_q1_0_g128; | |
| static_assert(sizeof(block_q1_0_g128) == sizeof(ggml_half) + QK1_0_g128/8, "wrong q1_0_g128 block size/padding"); | |
| #define QK1_0_G128 128 | |
| typedef struct { | |
| ggml_half d; // shared scale | |
| uint8_t qs[QK1_0_G128/8]; // sign bits | |
| } block_q1_0_g128; | |
| static_assert(sizeof(block_q1_0_g128) == sizeof(ggml_half) + QK1_0_G128/8, "wrong q1_0_g128 block size/padding"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,10 @@ void quantize_row_q5_1(const float * GGML_RESTRICT x, void * GGML_RESTRICT y, in | |
| quantize_row_q5_1_ref(x, y, k); | ||
| } | ||
|
|
||
| void quantize_row_q1_0_g128(const float * GGML_RESTRICT x, void * GGML_RESTRICT y, int64_t k) { | ||
| quantize_row_q1_0_g128_ref(x, y, k); | ||
| } | ||
|
|
||
| void quantize_row_q8_0_generic(const float * GGML_RESTRICT x, void * GGML_RESTRICT y, int64_t k) { | ||
| quantize_row_q8_0_ref(x, y, k); | ||
| } | ||
|
|
@@ -332,6 +336,127 @@ void ggml_vec_dot_q8_0_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, c | |
| *s = sumf; | ||
| } | ||
|
|
||
| void ggml_vec_dot_q1_0_g128_q8_0_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) { | ||
| static_assert(QK1_0_g128 % QK8_0 == 0, "Q1_0_g128 must align with Q8_0 blocks"); | ||
|
|
||
| const int blocks_per_q1 = QK1_0_g128 / QK8_0; | ||
| const int nb = n / QK1_0_g128; | ||
|
|
||
| assert(n % QK1_0_g128 == 0); | ||
|
Comment on lines
+339
to
+345
|
||
| assert(nrc == 1); | ||
| UNUSED(nrc); | ||
| UNUSED(bx); | ||
| UNUSED(by); | ||
| UNUSED(bs); | ||
|
|
||
| const block_q1_0_g128 * GGML_RESTRICT x = vx; | ||
| const block_q8_0 * GGML_RESTRICT y = vy; | ||
|
|
||
| float sumf = 0.0f; | ||
|
|
||
| for (int ib = 0; ib < nb; ++ib) { | ||
| float block_sum = 0.0f; | ||
|
|
||
| for (int jb = 0; jb < blocks_per_q1; ++jb) { | ||
| const block_q8_0 * yb = &y[ib*blocks_per_q1 + jb]; | ||
| int sumi = 0; | ||
|
|
||
| for (int j = 0; j < QK8_0; ++j) { | ||
| const int idx = jb*QK8_0 + j; | ||
| const int sign = ((x[ib].qs[idx / 8] >> (idx % 8)) & 1) ? 1 : -1; | ||
| sumi += sign * yb->qs[j]; | ||
| } | ||
|
|
||
| block_sum += GGML_CPU_FP16_TO_FP32(yb->d) * (float) sumi; | ||
| } | ||
|
|
||
| sumf += GGML_CPU_FP16_TO_FP32(x[ib].d) * block_sum; | ||
| } | ||
|
|
||
| *s = sumf; | ||
| } | ||
|
|
||
| #if defined(__ARM_NEON) | ||
| static const uint8_t k_q1_0_g128_bit_masks[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; | ||
|
|
||
| static inline int8x8_t ggml_q1_0_g128_signs_neon(uint8_t packed_signs) { | ||
| const uint8x8_t sign_bits = vcgt_u8( | ||
| vand_u8(vdup_n_u8(packed_signs), vld1_u8(k_q1_0_g128_bit_masks)), | ||
| vdup_n_u8(0) | ||
| ); | ||
| return vreinterpret_s8_u8( | ||
| vbsl_u8( | ||
| sign_bits, | ||
| vreinterpret_u8_s8(vdup_n_s8(1)), | ||
| vreinterpret_u8_s8(vdup_n_s8(-1)) | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| static inline int32_t ggml_vec_dot_q1_0_g128_q8_0_neon_16(int8x16_t signs, int8x16_t activations) { | ||
| #if defined(__ARM_FEATURE_DOTPROD) | ||
| return vaddvq_s32(ggml_vdotq_s32(vdupq_n_s32(0), signs, activations)); | ||
| #else | ||
| const int16x8_t prod_lo = vmull_s8(vget_low_s8(signs), vget_low_s8(activations)); | ||
| const int16x8_t prod_hi = vmull_s8(vget_high_s8(signs), vget_high_s8(activations)); | ||
| return vaddlvq_s16(vaddq_s16(prod_lo, prod_hi)); | ||
| #endif | ||
| } | ||
| #endif | ||
|
|
||
| void ggml_vec_dot_q1_0_g128_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) { | ||
| #if defined(__ARM_NEON) | ||
| static_assert(QK1_0_g128 % QK8_0 == 0, "Q1_0_g128 must align with Q8_0 blocks"); | ||
|
|
||
| const int blocks_per_q1 = QK1_0_g128 / QK8_0; | ||
| const int nb = n / QK1_0_g128; | ||
|
|
||
| assert(n % QK1_0_g128 == 0); | ||
| assert(nrc == 1); | ||
| UNUSED(nrc); | ||
| UNUSED(bx); | ||
| UNUSED(by); | ||
| UNUSED(bs); | ||
|
|
||
| const block_q1_0_g128 * GGML_RESTRICT x = vx; | ||
| const block_q8_0 * GGML_RESTRICT y = vy; | ||
|
|
||
| float sumf = 0.0f; | ||
|
|
||
| for (int ib = 0; ib < nb; ++ib) { | ||
| float block_sum = 0.0f; | ||
|
|
||
| for (int jb = 0; jb < blocks_per_q1; ++jb) { | ||
| const block_q8_0 * yb = &y[ib*blocks_per_q1 + jb]; | ||
| const uint8_t * packed_signs = x[ib].qs + jb*(QK8_0 / 8); | ||
|
|
||
| const int8x16_t signs0 = vcombine_s8( | ||
| ggml_q1_0_g128_signs_neon(packed_signs[0]), | ||
| ggml_q1_0_g128_signs_neon(packed_signs[1]) | ||
| ); | ||
| const int8x16_t signs1 = vcombine_s8( | ||
| ggml_q1_0_g128_signs_neon(packed_signs[2]), | ||
| ggml_q1_0_g128_signs_neon(packed_signs[3]) | ||
| ); | ||
| const int8x16_t qy0 = vld1q_s8(yb->qs); | ||
| const int8x16_t qy1 = vld1q_s8(yb->qs + 16); | ||
|
|
||
| const int sumi = | ||
| ggml_vec_dot_q1_0_g128_q8_0_neon_16(signs0, qy0) + | ||
| ggml_vec_dot_q1_0_g128_q8_0_neon_16(signs1, qy1); | ||
|
|
||
| block_sum += GGML_CPU_FP16_TO_FP32(yb->d) * (float) sumi; | ||
| } | ||
|
|
||
| sumf += GGML_CPU_FP16_TO_FP32(x[ib].d) * block_sum; | ||
| } | ||
|
|
||
| *s = sumf; | ||
| #else | ||
| ggml_vec_dot_q1_0_g128_q8_0_generic(n, s, bs, vx, bx, vy, by, nrc); | ||
| #endif | ||
| } | ||
|
|
||
| void ggml_vec_dot_tq1_0_q8_K_generic(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc) { | ||
| assert(nrc == 1); | ||
| UNUSED(nrc); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -221,6 +221,29 @@ void quantize_row_q8_0_ref(const float * GGML_RESTRICT x, block_q8_0 * GGML_REST | |
| } | ||
| } | ||
|
|
||
| // reference implementation for deterministic creation of model files | ||
| void quantize_row_q1_0_g128_ref(const float * GGML_RESTRICT x, block_q1_0_g128 * GGML_RESTRICT y, int64_t k) { | ||
| assert(k % QK1_0_g128 == 0); | ||
| const int nb = k / QK1_0_g128; | ||
|
|
||
| for (int i = 0; i < nb; ++i) { | ||
| float amax = 0.0f; | ||
|
|
||
| for (int j = 0; j < QK1_0_g128; ++j) { | ||
| amax = MAX(amax, fabsf(x[i*QK1_0_g128 + j])); | ||
| } | ||
|
|
||
| y[i].d = GGML_FP32_TO_FP16(amax); | ||
| memset(y[i].qs, 0, sizeof(y[i].qs)); | ||
|
Comment on lines
+225
to
+237
|
||
|
|
||
| for (int j = 0; j < QK1_0_g128; ++j) { | ||
| if (x[i*QK1_0_g128 + j] >= 0.0f) { | ||
| y[i].qs[j / 8] |= (uint8_t) (1u << (j % 8)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // reference implementation for deterministic creation of model files | ||
| void quantize_row_q8_1_ref(const float * GGML_RESTRICT x, block_q8_1 * GGML_RESTRICT y, int64_t k) { | ||
| assert(QK8_1 == 32); | ||
|
|
@@ -414,6 +437,21 @@ void dequantize_row_q8_0(const block_q8_0 * GGML_RESTRICT x, float * GGML_RESTRI | |
| } | ||
| } | ||
|
|
||
| void dequantize_row_q1_0_g128(const block_q1_0_g128 * GGML_RESTRICT x, float * GGML_RESTRICT y, int64_t k) { | ||
| assert(k % QK1_0_g128 == 0); | ||
|
|
||
| const int nb = k / QK1_0_g128; | ||
|
|
||
| for (int i = 0; i < nb; ++i) { | ||
| const float d = GGML_FP16_TO_FP32(x[i].d); | ||
|
|
||
| for (int j = 0; j < QK1_0_g128; ++j) { | ||
| const int bit = (x[i].qs[j / 8] >> (j % 8)) & 1; | ||
| y[i*QK1_0_g128 + j] = bit ? d : -d; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void dequantize_row_mxfp4(const block_mxfp4 * GGML_RESTRICT x, float * GGML_RESTRICT y, int64_t k) { | ||
| static const int qk = QK_MXFP4; | ||
|
|
||
|
|
@@ -2092,6 +2130,13 @@ size_t quantize_q8_0(const float * GGML_RESTRICT src, void * GGML_RESTRICT dst, | |
| return nrow * row_size; | ||
| } | ||
|
|
||
| size_t quantize_q1_0_g128(const float * GGML_RESTRICT src, void * GGML_RESTRICT dst, int64_t nrow, int64_t n_per_row, const float * quant_weights) { | ||
| GGML_UNUSED(quant_weights); | ||
| const size_t row_size = ggml_row_size(GGML_TYPE_Q1_0_g128, n_per_row); | ||
| quantize_row_q1_0_g128_ref(src, dst, (int64_t) nrow*n_per_row); | ||
| return nrow * row_size; | ||
| } | ||
|
|
||
| size_t quantize_mxfp4(const float * GGML_RESTRICT src, void * GGML_RESTRICT dst, int64_t nrow, int64_t n_per_row, const float * quant_weights) { | ||
| GGML_UNUSED(quant_weights); | ||
| quantize_row_mxfp4_ref(src, dst, (int64_t)nrow*n_per_row); | ||
|
|
@@ -5221,6 +5266,10 @@ bool ggml_validate_row_data(enum ggml_type type, const void * data, size_t nbyte | |
| { | ||
| VALIDATE_ROW_DATA_D_F16_IMPL(block_q8_0, data, nb); | ||
| } break; | ||
| case GGML_TYPE_Q1_0_g128: | ||
| { | ||
| VALIDATE_ROW_DATA_D_F16_IMPL(block_q1_0_g128, data, nb); | ||
| } break; | ||
| case GGML_TYPE_MXFP4: | ||
| { | ||
| VALIDATE_ROW_DATA_E_E8M0_IMPL(block_mxfp4, data, nb); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enum introduces a hole at value 40 (
MXFP4 = 39thenQ1_0_g128 = 41), which makesGGML_TYPE_COUNT(42) larger than the number of actually-defined type IDs in this enum excerpt. Code that iteratesfor (t = 0; t < GGML_TYPE_COUNT; ++t)may now hit an undefined/unused type value (40) with zero-initialized traits, causing incorrect behavior. Recommended fix: assignGGML_TYPE_Q1_0_g128 = 40andGGML_TYPE_COUNT = 41, unless 40 is intentionally reserved—if it is reserved, add an explicit placeholder enum value/comment and ensure the traits/dispatch logic handle that reserved ID safely.