1717#endif
1818
1919namespace piquant {
20- // (All types except u/int4) Amount of standard deviations/sigmas (left and right of 0) to use for the quantization range
21- static constexpr double stddev_scale {12.0 };
22-
23- // (u/int4 only) Amount of standard deviations/sigmas (left and right of 0) to use for the quantization range
24- static constexpr double stddev_scale_int4 {2.7 };
25-
2620 enum class round_mode {
2721 nearest,
28- stochastic
22+ stochastic,
23+
24+ count_
2925 };
3026
3127 enum class reduce_op {
3228 set, // output[i] = dequantize(input[i])
3329 add, // output[i] += qdeuantize(input[i])
30+
31+ count_
3432 };
3533
34+ // All supported data types for quantization and dequantization. Order matters.
3635 enum class dtype {
36+ f32 = 0 ,
37+ f64 ,
38+ uint2,
39+ int2,
3740 uint4,
3841 int4,
3942 uint8,
@@ -44,46 +47,65 @@ namespace piquant {
4447 int32,
4548 uint64,
4649 int64,
47- f32 ,
48- f64 ,
4950
50- num_
51+ count_
52+ };
53+ static_assert (static_cast <std::underlying_type_t <dtype>>(dtype::count_) <= 0xff );
54+ static_assert (static_cast <std::underlying_type_t <dtype>>(dtype::f32 ) == 0 );
55+ static_assert (static_cast <std::underlying_type_t <dtype>>(dtype::f64 ) == 1 );
56+
57+ constexpr std::size_t float_dtype_count {2 }; // Exclude quantized types
58+
59+ struct uint2_t final {
60+ using packed_storage = std::uint8_t ;
61+ packed_storage bits;
62+
63+ constexpr uint2_t () noexcept : bits{} {}
64+ constexpr uint2_t (int u8 ) noexcept : bits{static_cast <packed_storage>(u8 )} {}
65+ constexpr auto operator == (uint2_t rhs) const noexcept -> bool { return bits == rhs.bits ; }
66+ constexpr auto operator != (uint2_t rhs) const noexcept -> bool { return !(*this == rhs); }
67+ constexpr auto operator == (packed_storage rhs) const noexcept -> bool { return bits == rhs; }
68+ constexpr auto operator != (packed_storage rhs) const noexcept -> bool { return !(*this == rhs); }
69+ };
70+
71+ struct int2_t final {
72+ using packed_storage = std::int8_t ;
73+ packed_storage bits;
74+
75+ constexpr int2_t () noexcept : bits{} {}
76+ constexpr int2_t (int u8 ) noexcept : bits{static_cast <packed_storage>(u8 )} {}
77+ constexpr auto operator == (int2_t rhs) const noexcept -> bool { return bits == rhs.bits ; }
78+ constexpr auto operator != (int2_t rhs) const noexcept -> bool { return !(*this == rhs); }
79+ constexpr auto operator == (packed_storage rhs) const noexcept -> bool { return bits == rhs; }
80+ constexpr auto operator != (packed_storage rhs) const noexcept -> bool { return !(*this == rhs); }
5181 };
52- static_assert (static_cast <std::underlying_type_t <dtype>>(dtype::num_) <= 0xff );
5382
5483 struct uint4_t final {
55- std::uint8_t u8 ;
56- constexpr uint4_t () noexcept : u8 {} {}
57- constexpr uint4_t (int u8 ) noexcept : u8 {static_cast <std::uint8_t >(u8 )} {}
58- constexpr auto operator == (std::uint8_t y) const noexcept -> bool { return this ->u8 == y; }
59- constexpr auto operator != (std::uint8_t y) const noexcept -> bool { return !(*this == y); }
60- constexpr auto operator == (uint4_t y) const noexcept -> bool { return this ->u8 == y.u8 ; }
61- constexpr auto operator != (uint4_t y) const noexcept -> bool { return !(*this == y); }
62- constexpr auto pack (std::uint8_t lo, std::uint8_t hi) noexcept -> void {
63- u8 = static_cast <std::uint8_t >((lo & 15 ) | ((hi & 15 ) << 4 ));
64- }
65- [[nodiscard]] constexpr auto unpack () const noexcept -> std::array<std::uint8_t, 2> {
66- return {static_cast <std::uint8_t >(u8 & 15 ), static_cast <std::uint8_t >(u8 >> 4 )};
67- }
84+ using packed_storage = std::uint8_t ;
85+ packed_storage bits;
86+
87+ constexpr uint4_t () noexcept : bits {} {}
88+ constexpr uint4_t (int u8 ) noexcept : bits {static_cast <packed_storage>(u8 )} {}
89+ constexpr auto operator == (uint4_t rhs) const noexcept -> bool { return bits == rhs.bits ; }
90+ constexpr auto operator != (uint4_t rhs) const noexcept -> bool { return !(*this == rhs); }
91+ constexpr auto operator == (packed_storage rhs) const noexcept -> bool { return bits == rhs; }
92+ constexpr auto operator != (packed_storage rhs) const noexcept -> bool { return !(*this == rhs); }
6893 };
6994
7095 struct int4_t final {
71- std::int8_t u8 ;
72- constexpr int4_t () noexcept : u8 {} {}
73- constexpr int4_t (int u8 ) noexcept : u8 {static_cast <std::int8_t >(u8 )} {}
74- constexpr auto operator == (std::int8_t u8 ) const noexcept -> bool { return this ->u8 == u8 ; }
75- constexpr auto operator != (std::int8_t y) const noexcept -> bool { return !(*this == y); }
76- constexpr auto operator == (int4_t y) const noexcept -> bool { return this ->u8 == y.u8 ; }
77- constexpr auto operator != (int4_t y) const noexcept -> bool { return !(*this == y); }
78- constexpr auto pack (std::int8_t lo, std::int8_t hi) noexcept -> void {
79- u8 = static_cast <std::int8_t >((lo & 15 ) | ((hi & 15 ) << 4 ));
80- }
81- [[nodiscard]] constexpr auto unpack () const noexcept -> std::array<std::int8_t, 2> {
82- constexpr auto snex4 {[](std::int8_t x) noexcept -> std::int8_t { return x & 0x8 ? static_cast <std::int8_t >(x|0xF0 ) : x; }};
83- return {(snex4 (u8 & 15 )), (snex4 (u8 >> 4 ))};
84- }
96+ using packed_storage = std::int8_t ;
97+ packed_storage bits;
98+
99+ constexpr int4_t () noexcept : bits {} {}
100+ constexpr int4_t (int u8 ) noexcept : bits {static_cast <packed_storage>(u8 )} {}
101+ constexpr auto operator == (int4_t rhs) const noexcept -> bool { return bits == rhs.bits ; }
102+ constexpr auto operator != (int4_t rhs) const noexcept -> bool { return !(*this == rhs); }
103+ constexpr auto operator == (packed_storage rhs) const noexcept -> bool { return bits == rhs; }
104+ constexpr auto operator != (packed_storage rhs) const noexcept -> bool { return !(*this == rhs); }
85105 };
86106
107+ static_assert (sizeof (uint2_t ) == 1 );
108+ static_assert (sizeof (int2_t ) == 1 );
87109 static_assert (sizeof (uint4_t ) == 1 );
88110 static_assert (sizeof (int4_t ) == 1 );
89111
@@ -106,6 +128,10 @@ namespace piquant {
106128 };
107129
108130 constexpr std::array dtype_infos {
131+ dtype_info{.name =" f32" , .stride =4 , .bit_size =32 , .flags =dtype_flags::is_float+dtype_flags::is_signed}, // f32
132+ dtype_info{.name =" f64" , .stride =8 , .bit_size =64 , .flags =dtype_flags::is_float+dtype_flags::is_signed}, // f64
133+ dtype_info{.name =" uint2" , .stride =1 , .bit_size =2 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_packed}, // uint2
134+ dtype_info{.name =" int2" , .stride =1 , .bit_size =2 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_packed+dtype_flags::is_signed}, // int2
109135 dtype_info{.name =" uint4" , .stride =1 , .bit_size =4 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_packed}, // uint4
110136 dtype_info{.name =" int4" , .stride =1 , .bit_size =4 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_packed+dtype_flags::is_signed}, // int4
111137 dtype_info{.name =" uint8" , .stride =1 , .bit_size =8 , .flags =dtype_flags::is_quant+dtype_flags::is_int}, // uint8
@@ -115,9 +141,7 @@ namespace piquant {
115141 dtype_info{.name =" uint32" , .stride =4 , .bit_size =32 , .flags =dtype_flags::is_quant+dtype_flags::is_int}, // uint32
116142 dtype_info{.name =" int32" , .stride =4 , .bit_size =32 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_signed}, // int32
117143 dtype_info{.name =" uint64" , .stride =8 , .bit_size =64 , .flags =dtype_flags::is_quant+dtype_flags::is_int}, // uint64
118- dtype_info{.name =" int64" , .stride =8 , .bit_size =64 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_signed}, // int64
119- dtype_info{.name =" f32" , .stride =4 , .bit_size =32 , .flags =dtype_flags::is_float+dtype_flags::is_signed}, // f32
120- dtype_info{.name =" f64" , .stride =8 , .bit_size =64 , .flags =dtype_flags::is_float+dtype_flags::is_signed}, // f64
144+ dtype_info{.name =" int64" , .stride =8 , .bit_size =64 , .flags =dtype_flags::is_quant+dtype_flags::is_int+dtype_flags::is_signed} // int64
121145 };
122146 static_assert ([]() -> bool {
123147 for (auto && info : dtype_infos) {
@@ -132,19 +156,31 @@ namespace piquant {
132156 static constexpr T min{std::numeric_limits<T>::min ()};
133157 static constexpr T max{std::numeric_limits<T>::max ()};
134158 };
159+ template <> struct dtype_limits <uint2_t > final {
160+ static constexpr std::uint8_t min{0 };
161+ static constexpr std::uint8_t max{3 };
162+ };
163+ template <> struct dtype_limits <int2_t > final {
164+ static constexpr std::int8_t min{-2 };
165+ static constexpr std::int8_t max{1 };
166+ };
135167 template <> struct dtype_limits <uint4_t > final {
136168 static constexpr std::uint8_t min{0 };
137- static constexpr std::uint8_t max{0xf };
169+ static constexpr std::uint8_t max{15 };
138170 };
139171 template <> struct dtype_limits <int4_t > final {
140- static constexpr std::int8_t min{-0x8 };
141- static constexpr std::int8_t max{0x7 };
172+ static constexpr std::int8_t min{-8 };
173+ static constexpr std::int8_t max{7 };
142174 };
143175
176+ template <typename T> concept is_int2 = std::is_same_v<T, uint2_t > || std::is_same_v<T, int2_t >;
144177 template <typename T> concept is_int4 = std::is_same_v<T, uint4_t > || std::is_same_v<T, int4_t >;
145- template <typename T> concept is_dtype = std::is_arithmetic_v<T> || is_int4<T>;
178+ template <typename T> concept is_packed_int = is_int2<T> || is_int4<T>;
179+ template <typename T> concept is_dtype = std::is_arithmetic_v<T> || is_packed_int<T>;
146180 template <typename T> requires is_dtype<T> struct dtype_traits final {};
147181
182+ template <> struct dtype_traits <uint2_t > { static constexpr dtype type_code = dtype::uint2; };
183+ template <> struct dtype_traits <int2_t > { static constexpr dtype type_code = dtype::int2; };
148184 template <> struct dtype_traits <uint4_t > { static constexpr dtype type_code = dtype::uint4; };
149185 template <> struct dtype_traits <int4_t > { static constexpr dtype type_code = dtype::int4; };
150186 template <> struct dtype_traits <std::int8_t > { static constexpr dtype type_code = dtype::int8; };
@@ -283,16 +319,16 @@ namespace piquant {
283319 };
284320
285321 struct quant_descriptor final {
286- command_type type{command_type::quant};
287- const std::byte* in{};
288- std::byte* out{};
289- std::int64_t numel{};
322+ command_type type {command_type::quant};
323+ const std::byte* in {};
324+ std::byte* out {};
325+ std::int64_t numel {};
290326 float scale{};
291- std::int64_t zero_point{};
292- dtype dt_in{};
293- dtype dt_out{};
294- round_mode rnd_mode {};
295- reduce_op reduce {};
327+ std::int64_t zero_point {};
328+ dtype dt_in {};
329+ dtype dt_out {};
330+ round_mode rounding {};
331+ reduce_op reducing {};
296332 };
297333
298334 private:
0 commit comments