@@ -255,8 +255,9 @@ std::tuple<float, float> _fast_gmm_cdf(
255255 const std::array<float , K> &weights) {
256256 float cdf1 = 0.0 , cdf2 = 0.0 ;
257257
258- // Check if SIMD path is enabled AND applicable (K==4)
258+ // Check if SIMD path is enabled AND applicable (K==3 or K== 4)
259259 if (use_simd_path () && K == 4 ) {
260+ // K=4: Use full 128-bit lanes (4 floats each)
260261 __m128 x1Simd = _mm_set1_ps (static_cast <float >(x1));
261262 __m128 x2Simd = _mm_set1_ps (static_cast <float >(x2));
262263 __m256 x1x2Simd = _mm256_set_m128 (x1Simd, x2Simd);
@@ -282,7 +283,35 @@ std::tuple<float, float> _fast_gmm_cdf(
282283 cdf2Simd_parts = _mm_hadd_ps (cdf2Simd_parts, cdf2Simd_parts);
283284 cdf2 = _mm_cvtss_f32 (cdf2Simd_parts);
284285
285- } else { // Generic loop for non-SIMD path or K != 4
286+ } else if (use_simd_path () && K == 3 ) {
287+ // K=3: Load 3 values + 1 padding (set weight to 0 for 4th element)
288+ __m128 x1Simd = _mm_set1_ps (static_cast <float >(x1));
289+ __m128 x2Simd = _mm_set1_ps (static_cast <float >(x2));
290+ __m256 x1x2Simd = _mm256_set_m128 (x1Simd, x2Simd);
291+
292+ // Load 3 values and pad with 0 for means/scales, 0 for weights
293+ __m128 meansHalf = _mm_set_ps (0 .0f , means[2 ], means[1 ], means[0 ]);
294+ __m256 meansSimd = _mm256_set_m128 (meansHalf, meansHalf);
295+ __m128 scalesHalf = _mm_set_ps (1 .0f , scales[2 ], scales[1 ], scales[0 ]); // scale=1 to avoid div by 0
296+ __m256 scalesSimd = _mm256_set_m128 (scalesHalf, scalesHalf);
297+ __m128 weightsHalf = _mm_set_ps (0 .0f , weights[2 ], weights[1 ], weights[0 ]); // weight=0 so 4th doesn't contribute
298+ __m256 weightsSimd = _mm256_set_m128 (weightsHalf, weightsHalf);
299+
300+ __m256 x1x2Normalized = _mm256_div_ps (_mm256_sub_ps (x1x2Simd, meansSimd), scalesSimd);
301+ __m256 cdfs = _mm256_mul_ps (weightsSimd, _fast_gaussian_cdf (x1x2Normalized));
302+
303+ __m128 cdf2Simd_parts = _mm256_castps256_ps128 (cdfs);
304+ __m128 cdf1Simd_parts = _mm256_extractf128_ps (cdfs, 1 );
305+
306+ cdf1Simd_parts = _mm_hadd_ps (cdf1Simd_parts, cdf1Simd_parts);
307+ cdf1Simd_parts = _mm_hadd_ps (cdf1Simd_parts, cdf1Simd_parts);
308+ cdf1 = _mm_cvtss_f32 (cdf1Simd_parts);
309+
310+ cdf2Simd_parts = _mm_hadd_ps (cdf2Simd_parts, cdf2Simd_parts);
311+ cdf2Simd_parts = _mm_hadd_ps (cdf2Simd_parts, cdf2Simd_parts);
312+ cdf2 = _mm_cvtss_f32 (cdf2Simd_parts);
313+
314+ } else { // Generic loop for non-SIMD path or other K values
286315 for (int i = 0 ; i < K; ++i){
287316 cdf1 += weights[i] * _fast_gaussian_cdf ((x1 - means[i])/scales[i]);
288317 cdf2 += weights[i] * _fast_gaussian_cdf ((x2 - means[i])/scales[i]);
@@ -1058,6 +1087,78 @@ torch::Tensor RansDecoder::decode_stream_gmm(
10581087// K_gmm_default is already defined above
10591088// constexpr int K = 4; // Replaced by K_gmm_default for pybind context
10601089
1090+ // Helper functions to dispatch based on runtime K value
1091+ void BufferedRansEncoder_encode_with_indexes_gmm_dispatch (
1092+ BufferedRansEncoder& self,
1093+ const torch::Tensor &symbols, const torch::Tensor &scales,
1094+ const torch::Tensor &means, const torch::Tensor &weights,
1095+ const int32_t max_value, const int32_t K) {
1096+ switch (K) {
1097+ case 1 : self.encode_with_indexes_gmm <1 >(symbols, scales, means, weights, max_value); break ;
1098+ case 2 : self.encode_with_indexes_gmm <2 >(symbols, scales, means, weights, max_value); break ;
1099+ case 3 : self.encode_with_indexes_gmm <3 >(symbols, scales, means, weights, max_value); break ;
1100+ case 4 : self.encode_with_indexes_gmm <4 >(symbols, scales, means, weights, max_value); break ;
1101+ case 5 : self.encode_with_indexes_gmm <5 >(symbols, scales, means, weights, max_value); break ;
1102+ case 6 : self.encode_with_indexes_gmm <6 >(symbols, scales, means, weights, max_value); break ;
1103+ case 7 : self.encode_with_indexes_gmm <7 >(symbols, scales, means, weights, max_value); break ;
1104+ case 8 : self.encode_with_indexes_gmm <8 >(symbols, scales, means, weights, max_value); break ;
1105+ default : throw std::runtime_error (" Unsupported K value: " + std::to_string (K) + " . K must be 1-8." );
1106+ }
1107+ }
1108+
1109+ py::bytes RansEncoder_encode_with_indexes_gmm_dispatch (
1110+ RansEncoder& self,
1111+ const torch::Tensor &symbols, const torch::Tensor &scales,
1112+ const torch::Tensor &means, const torch::Tensor &weights,
1113+ const int32_t max_value, const int32_t K) {
1114+ switch (K) {
1115+ case 1 : return self.encode_with_indexes_gmm <1 >(symbols, scales, means, weights, max_value);
1116+ case 2 : return self.encode_with_indexes_gmm <2 >(symbols, scales, means, weights, max_value);
1117+ case 3 : return self.encode_with_indexes_gmm <3 >(symbols, scales, means, weights, max_value);
1118+ case 4 : return self.encode_with_indexes_gmm <4 >(symbols, scales, means, weights, max_value);
1119+ case 5 : return self.encode_with_indexes_gmm <5 >(symbols, scales, means, weights, max_value);
1120+ case 6 : return self.encode_with_indexes_gmm <6 >(symbols, scales, means, weights, max_value);
1121+ case 7 : return self.encode_with_indexes_gmm <7 >(symbols, scales, means, weights, max_value);
1122+ case 8 : return self.encode_with_indexes_gmm <8 >(symbols, scales, means, weights, max_value);
1123+ default : throw std::runtime_error (" Unsupported K value: " + std::to_string (K) + " . K must be 1-8." );
1124+ }
1125+ }
1126+
1127+ torch::Tensor RansDecoder_decode_with_indexes_gmm_dispatch (
1128+ RansDecoder& self,
1129+ const std::string &encoded,
1130+ const torch::Tensor &scales, const torch::Tensor &means,
1131+ const torch::Tensor &weights, const int32_t max_bs_value, const int32_t K) {
1132+ switch (K) {
1133+ case 1 : return self.decode_with_indexes_gmm <1 >(encoded, scales, means, weights, max_bs_value);
1134+ case 2 : return self.decode_with_indexes_gmm <2 >(encoded, scales, means, weights, max_bs_value);
1135+ case 3 : return self.decode_with_indexes_gmm <3 >(encoded, scales, means, weights, max_bs_value);
1136+ case 4 : return self.decode_with_indexes_gmm <4 >(encoded, scales, means, weights, max_bs_value);
1137+ case 5 : return self.decode_with_indexes_gmm <5 >(encoded, scales, means, weights, max_bs_value);
1138+ case 6 : return self.decode_with_indexes_gmm <6 >(encoded, scales, means, weights, max_bs_value);
1139+ case 7 : return self.decode_with_indexes_gmm <7 >(encoded, scales, means, weights, max_bs_value);
1140+ case 8 : return self.decode_with_indexes_gmm <8 >(encoded, scales, means, weights, max_bs_value);
1141+ default : throw std::runtime_error (" Unsupported K value: " + std::to_string (K) + " . K must be 1-8." );
1142+ }
1143+ }
1144+
1145+ torch::Tensor RansDecoder_decode_stream_gmm_dispatch (
1146+ RansDecoder& self,
1147+ const torch::Tensor &scales, const torch::Tensor &means,
1148+ const torch::Tensor &weights, const int32_t max_bs_value, const int32_t K) {
1149+ switch (K) {
1150+ case 1 : return self.decode_stream_gmm <1 >(scales, means, weights, max_bs_value);
1151+ case 2 : return self.decode_stream_gmm <2 >(scales, means, weights, max_bs_value);
1152+ case 3 : return self.decode_stream_gmm <3 >(scales, means, weights, max_bs_value);
1153+ case 4 : return self.decode_stream_gmm <4 >(scales, means, weights, max_bs_value);
1154+ case 5 : return self.decode_stream_gmm <5 >(scales, means, weights, max_bs_value);
1155+ case 6 : return self.decode_stream_gmm <6 >(scales, means, weights, max_bs_value);
1156+ case 7 : return self.decode_stream_gmm <7 >(scales, means, weights, max_bs_value);
1157+ case 8 : return self.decode_stream_gmm <8 >(scales, means, weights, max_bs_value);
1158+ default : throw std::runtime_error (" Unsupported K value: " + std::to_string (K) + " . K must be 1-8." );
1159+ }
1160+ }
1161+
10611162PYBIND11_MODULE (ans, m) {
10621163 m.attr (" __name__" ) = " compressai.ans" ;
10631164 m.doc () = " range Asymmetric Numeral System python bindings" ;
@@ -1075,12 +1176,8 @@ PYBIND11_MODULE(ans, m) {
10751176 const std::vector<int32_t > &, const std::vector<float > &, const int32_t >(
10761177 &BufferedRansEncoder::encode_with_indexes))
10771178 .def (" encode_with_indexes_gmm" ,
1078- static_cast <void (BufferedRansEncoder::*) (
1079- const torch::Tensor &, const torch::Tensor &, // Changed types
1080- const torch::Tensor &, const torch::Tensor &, // Changed types
1081- const int32_t )>(
1082- &BufferedRansEncoder::encode_with_indexes_gmm<K_gmm_default>), // Use K_gmm_default
1083- py::arg (" symbols" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_value" ))
1179+ &BufferedRansEncoder_encode_with_indexes_gmm_dispatch,
1180+ py::arg (" symbols" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_value" ), py::arg (" K" ) = K_gmm_default)
10841181 .def (" flush" , &BufferedRansEncoder::flush);
10851182
10861183 py::class_<RansEncoder>(m, " RansEncoder" )
@@ -1096,12 +1193,8 @@ PYBIND11_MODULE(ans, m) {
10961193 const std::vector<int32_t > &, const std::vector<float > &, const int32_t >(
10971194 &RansEncoder::encode_with_indexes))
10981195 .def (" encode_with_indexes_gmm" ,
1099- static_cast <py::bytes (RansEncoder::*) (
1100- const torch::Tensor &, const torch::Tensor &, // Changed types
1101- const torch::Tensor &, const torch::Tensor &, // Changed types
1102- const int32_t )>(
1103- &RansEncoder::encode_with_indexes_gmm<K_gmm_default>), // Use K_gmm_default
1104- py::arg (" symbols" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_value" ));
1196+ &RansEncoder_encode_with_indexes_gmm_dispatch,
1197+ py::arg (" symbols" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_value" ), py::arg (" K" ) = K_gmm_default);
11051198
11061199 py::class_<RansDecoder>(m, " RansDecoder" )
11071200 .def (py::init<>())
@@ -1124,22 +1217,11 @@ PYBIND11_MODULE(ans, m) {
11241217 &RansDecoder::decode_with_indexes),
11251218 " Decode a string to a list of symbols" )
11261219 .def (" decode_with_indexes_gmm" ,
1127- static_cast <torch::Tensor (RansDecoder::*) ( // Changed return type
1128- const std::string &,
1129- const torch::Tensor &, // Changed types
1130- const torch::Tensor &, // Changed types
1131- const torch::Tensor &, // Changed types
1132- const int32_t )>(
1133- &RansDecoder::decode_with_indexes_gmm<K_gmm_default>), // Use K_gmm_default
1220+ &RansDecoder_decode_with_indexes_gmm_dispatch,
11341221 " Decode a string to a tensor of symbols" ,
1135- py::arg (" encoded" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_bs_value" ))
1222+ py::arg (" encoded" ), py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_bs_value" ), py::arg ( " K " ) = K_gmm_default )
11361223 .def (" decode_stream_gmm" ,
1137- static_cast <torch::Tensor (RansDecoder::*) (
1138- const torch::Tensor &,
1139- const torch::Tensor &,
1140- const torch::Tensor &,
1141- const int32_t )>(
1142- &RansDecoder::decode_stream_gmm<K_gmm_default>),
1224+ &RansDecoder_decode_stream_gmm_dispatch,
11431225 " Decode stream to a tensor of symbols using GMM" ,
1144- py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_bs_value" ));
1226+ py::arg (" scales" ), py::arg (" means" ), py::arg (" weights" ), py::arg (" max_bs_value" ), py::arg ( " K " ) = K_gmm_default );
11451227}
0 commit comments