react-native-executorch 0.10.6 → 0.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/lib/module/fetcher/telemetry.js +1 -1
  2. package/package.json +1 -1
  3. package/react-native-executorch.podspec +2 -0
  4. package/src/fetcher/telemetry.ts +1 -1
  5. package/third-party/common/phonemis/CMakeLists.txt +70 -14
  6. package/third-party/common/phonemis/README.md +183 -33
  7. package/third-party/common/phonemis/THIRD_PARTY_NOTICES.md +57 -0
  8. package/third-party/common/phonemis/knowledge/code_style.md +642 -0
  9. package/third-party/common/phonemis/licenses/Unicode-3.0.txt +39 -0
  10. package/third-party/common/phonemis/licenses/misaki-Apache-2.0.txt +201 -0
  11. package/third-party/common/phonemis/licenses/nlohmann-json-MIT.txt +21 -0
  12. package/third-party/common/phonemis/licenses/xsimd-BSD-3-Clause.txt +29 -0
  13. package/third-party/common/phonemis/src/phonemis/base/config.h +6 -0
  14. package/third-party/common/phonemis/src/phonemis/base/ipipeline.cpp +75 -0
  15. package/third-party/common/phonemis/src/phonemis/base/ipipeline.h +21 -0
  16. package/third-party/common/phonemis/src/phonemis/base/phonemizer/constants.h +2 -5
  17. package/third-party/common/phonemis/src/phonemis/base/phonemizer/lexicon_phonemizer.cpp +3 -4
  18. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.cpp +17 -8
  19. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/neural_phonemizer.h +4 -0
  20. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.cpp +21 -6
  21. package/third-party/common/phonemis/src/phonemis/base/phonemizer/nn/tokenizer.h +13 -4
  22. package/third-party/common/phonemis/src/phonemis/base/pipeline.cpp +8 -0
  23. package/third-party/common/phonemis/src/phonemis/base/pipeline.h +19 -0
  24. package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.cpp +37 -0
  25. package/third-party/common/phonemis/src/phonemis/base/processor/common/conditions.h +45 -0
  26. package/third-party/common/phonemis/src/phonemis/base/processor/common/constants.h +10 -0
  27. package/third-party/common/phonemis/src/phonemis/base/processor/common/context.cpp +105 -0
  28. package/third-party/common/phonemis/src/phonemis/base/processor/common/context.h +49 -0
  29. package/third-party/common/phonemis/src/phonemis/base/processor/common/document.cpp +173 -0
  30. package/third-party/common/phonemis/src/phonemis/base/processor/common/document.h +67 -0
  31. package/third-party/common/phonemis/src/phonemis/base/processor/common/text.cpp +75 -0
  32. package/third-party/common/phonemis/src/phonemis/base/processor/common/text.h +60 -0
  33. package/third-party/common/phonemis/src/phonemis/base/processor/common/types.h +18 -0
  34. package/third-party/common/phonemis/src/phonemis/base/processor/config.h +53 -0
  35. package/third-party/common/phonemis/src/phonemis/base/processor/layer.h +9 -7
  36. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/constants.h +20 -0
  37. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.cpp +89 -0
  38. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/layer.h +35 -0
  39. package/third-party/common/phonemis/src/phonemis/base/processor/layers/normalize/types.h +16 -0
  40. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/kind.h +40 -0
  41. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/layer.h +382 -0
  42. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/notation.h +30 -0
  43. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.cpp +51 -0
  44. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/numerals.h +31 -0
  45. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern.h +30 -0
  46. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.cpp +17 -0
  47. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/pattern_set.h +31 -0
  48. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.cpp +811 -0
  49. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/patterns.h +285 -0
  50. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.cpp +64 -0
  51. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/primitives.h +23 -0
  52. package/third-party/common/phonemis/src/phonemis/base/processor/layers/num2word/types.h +94 -0
  53. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sanitizer/layer.cpp +19 -0
  54. package/third-party/common/phonemis/src/phonemis/base/processor/{sanitizer_layer.h → layers/sanitizer/layer.h} +8 -8
  55. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.cpp +65 -0
  56. package/third-party/common/phonemis/src/phonemis/base/processor/layers/sym2word/layer.h +29 -0
  57. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.cpp +170 -0
  58. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/layer.h +40 -0
  59. package/third-party/common/phonemis/src/phonemis/base/processor/layers/term2word/types.h +25 -0
  60. package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.cpp +47 -0
  61. package/third-party/common/phonemis/src/phonemis/base/processor/layers/trim/layer.h +19 -0
  62. package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.cpp +60 -0
  63. package/third-party/common/phonemis/src/phonemis/base/processor/preprocessor.h +45 -0
  64. package/third-party/common/phonemis/src/phonemis/base/processor/processor.cpp +6 -7
  65. package/third-party/common/phonemis/src/phonemis/base/processor/processor.h +14 -10
  66. package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.cpp +89 -52
  67. package/third-party/common/phonemis/src/phonemis/base/tagger/hmm_tagger.h +16 -9
  68. package/third-party/common/phonemis/src/phonemis/base/tokenizer/tokenizer.cpp +13 -12
  69. package/third-party/common/phonemis/src/phonemis/lang/de/constants.h +183 -19
  70. package/third-party/common/phonemis/src/phonemis/lang/de/num2word.cpp +355 -309
  71. package/third-party/common/phonemis/src/phonemis/lang/de/num2word.h +42 -17
  72. package/third-party/common/phonemis/src/phonemis/lang/de/patterns.cpp +56 -0
  73. package/third-party/common/phonemis/src/phonemis/lang/de/patterns.h +15 -0
  74. package/third-party/common/phonemis/src/phonemis/lang/de/pipeline.h +3 -3
  75. package/third-party/common/phonemis/src/phonemis/lang/en/constants.h +181 -30
  76. package/third-party/common/phonemis/src/phonemis/lang/en/hmm_tagger.cpp +5 -7
  77. package/third-party/common/phonemis/src/phonemis/lang/en/num2word.cpp +232 -135
  78. package/third-party/common/phonemis/src/phonemis/lang/en/num2word.h +41 -16
  79. package/third-party/common/phonemis/src/phonemis/lang/en/pipeline.h +3 -10
  80. package/third-party/common/phonemis/src/phonemis/lang/es/constants.h +182 -60
  81. package/third-party/common/phonemis/src/phonemis/lang/es/num2word.cpp +247 -212
  82. package/third-party/common/phonemis/src/phonemis/lang/es/num2word.h +38 -18
  83. package/third-party/common/phonemis/src/phonemis/lang/es/patterns.cpp +50 -0
  84. package/third-party/common/phonemis/src/phonemis/lang/es/patterns.h +15 -0
  85. package/third-party/common/phonemis/src/phonemis/lang/es/pipeline.h +3 -3
  86. package/third-party/common/phonemis/src/phonemis/lang/fr/constants.h +152 -26
  87. package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.cpp +252 -179
  88. package/third-party/common/phonemis/src/phonemis/lang/fr/num2word.h +38 -15
  89. package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.cpp +83 -0
  90. package/third-party/common/phonemis/src/phonemis/lang/fr/patterns.h +15 -0
  91. package/third-party/common/phonemis/src/phonemis/lang/fr/pipeline.h +3 -3
  92. package/third-party/common/phonemis/src/phonemis/lang/hi/constants.h +62 -10
  93. package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.cpp +74 -123
  94. package/third-party/common/phonemis/src/phonemis/lang/hi/num2word.h +27 -16
  95. package/third-party/common/phonemis/src/phonemis/lang/hi/pipeline.h +5 -3
  96. package/third-party/common/phonemis/src/phonemis/lang/it/constants.h +122 -42
  97. package/third-party/common/phonemis/src/phonemis/lang/it/num2word.cpp +200 -207
  98. package/third-party/common/phonemis/src/phonemis/lang/it/num2word.h +30 -18
  99. package/third-party/common/phonemis/src/phonemis/lang/it/patterns.cpp +25 -0
  100. package/third-party/common/phonemis/src/phonemis/lang/it/patterns.h +15 -0
  101. package/third-party/common/phonemis/src/phonemis/lang/it/pipeline.h +3 -3
  102. package/third-party/common/phonemis/src/phonemis/lang/pl/constants.h +346 -77
  103. package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.cpp +631 -246
  104. package/third-party/common/phonemis/src/phonemis/lang/pl/num2word.h +47 -15
  105. package/third-party/common/phonemis/src/phonemis/lang/pl/pipeline.h +6 -6
  106. package/third-party/common/phonemis/src/phonemis/lang/pt/constants.h +201 -0
  107. package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.cpp +365 -0
  108. package/third-party/common/phonemis/src/phonemis/lang/pt/num2word.h +53 -0
  109. package/third-party/common/phonemis/src/phonemis/lang/pt/pipeline.h +4 -2
  110. package/third-party/common/phonemis/src/phonemis/main.cpp +42 -11
  111. package/third-party/common/phonemis/src/phonemis/protophone/meta.h +24 -0
  112. package/third-party/common/phonemis/src/phonemis/protophone/numeric.h +26 -0
  113. package/third-party/common/phonemis/src/phonemis/protophone/protophone.cpp +525 -0
  114. package/third-party/common/phonemis/src/phonemis/protophone/protophone.h +80 -0
  115. package/third-party/common/phonemis/src/phonemis/protophone/tensor.h +76 -0
  116. package/third-party/common/phonemis/src/phonemis/protophone/tensor_utils.h +89 -0
  117. package/third-party/common/phonemis/src/phonemis/protophone/tensor_view.h +125 -0
  118. package/third-party/common/phonemis/src/phonemis/protophone/types.h +43 -0
  119. package/third-party/common/phonemis/src/phonemis/utils/strings.h +20 -14
  120. package/third-party/common/phonemis/src/phonemis/utils/unicode.h +15 -1
  121. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_arithmetic.hpp +267 -0
  122. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_bit.hpp +232 -0
  123. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_cast.hpp +43 -0
  124. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_complex.hpp +112 -0
  125. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_details.hpp +386 -0
  126. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_logical.hpp +240 -0
  127. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_math.hpp +2601 -0
  128. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_memory.hpp +1006 -0
  129. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_rounding.hpp +72 -0
  130. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_swizzle.hpp +260 -0
  131. package/third-party/common/phonemis/src/third-party/xsimd/arch/common/xsimd_common_trigo.hpp +996 -0
  132. package/third-party/common/phonemis/src/third-party/xsimd/arch/utils/shifts.hpp +82 -0
  133. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx.hpp +2111 -0
  134. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2.hpp +1591 -0
  135. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx2_128.hpp +170 -0
  136. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512bw.hpp +724 -0
  137. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512cd.hpp +28 -0
  138. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512dq.hpp +270 -0
  139. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512er.hpp +17 -0
  140. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512f.hpp +2919 -0
  141. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512ifma.hpp +17 -0
  142. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512pf.hpp +17 -0
  143. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi.hpp +76 -0
  144. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vbmi2.hpp +130 -0
  145. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512bw.hpp +17 -0
  146. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx512vnni_avx512vbmi2.hpp +17 -0
  147. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avx_128.hpp +164 -0
  148. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_avxvnni.hpp +17 -0
  149. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common.hpp +26 -0
  150. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_common_fwd.hpp +127 -0
  151. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_constants.hpp +393 -0
  152. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_emulated.hpp +787 -0
  153. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx.hpp +93 -0
  154. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_avx2.hpp +46 -0
  155. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma3_sse.hpp +97 -0
  156. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_fma4.hpp +92 -0
  157. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_i8mm_neon64.hpp +17 -0
  158. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_isa.hpp +148 -0
  159. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon.hpp +3580 -0
  160. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_neon64.hpp +1614 -0
  161. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_rvv.hpp +1515 -0
  162. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_scalar.hpp +1210 -0
  163. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse2.hpp +2333 -0
  164. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse3.hpp +66 -0
  165. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_1.hpp +510 -0
  166. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sse4_2.hpp +44 -0
  167. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_ssse3.hpp +186 -0
  168. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_sve.hpp +1186 -0
  169. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vsx.hpp +947 -0
  170. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_vxe.hpp +787 -0
  171. package/third-party/common/phonemis/src/third-party/xsimd/arch/xsimd_wasm.hpp +1819 -0
  172. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_arch.hpp +242 -0
  173. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_config.hpp +633 -0
  174. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features.hpp +50 -0
  175. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_arm.hpp +132 -0
  176. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_ppc.hpp +54 -0
  177. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_riscv.hpp +95 -0
  178. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_s390x.hpp +56 -0
  179. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpu_features_x86.hpp +1121 -0
  180. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_cpuid.hpp +144 -0
  181. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_getauxval.hpp +159 -0
  182. package/third-party/common/phonemis/src/third-party/xsimd/config/xsimd_macros.hpp +78 -0
  183. package/third-party/common/phonemis/src/third-party/xsimd/math/xsimd_rem_pio2.hpp +707 -0
  184. package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_aligned_allocator.hpp +349 -0
  185. package/third-party/common/phonemis/src/third-party/xsimd/memory/xsimd_alignment.hpp +102 -0
  186. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_all_registers.hpp +57 -0
  187. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_api.hpp +3106 -0
  188. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx2_register.hpp +57 -0
  189. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512bw_register.hpp +51 -0
  190. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512cd_register.hpp +51 -0
  191. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512dq_register.hpp +51 -0
  192. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512er_register.hpp +51 -0
  193. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512f_register.hpp +77 -0
  194. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512ifma_register.hpp +51 -0
  195. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512pf_register.hpp +51 -0
  196. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi2_register.hpp +51 -0
  197. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vbmi_register.hpp +51 -0
  198. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512bw_register.hpp +54 -0
  199. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx512vnni_avx512vbmi2_register.hpp +53 -0
  200. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avx_register.hpp +79 -0
  201. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_avxvnni_register.hpp +44 -0
  202. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch.hpp +1666 -0
  203. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_constant.hpp +572 -0
  204. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_batch_fwd.hpp +41 -0
  205. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_common_arch.hpp +49 -0
  206. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_emulated_register.hpp +80 -0
  207. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx2_register.hpp +50 -0
  208. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_avx_register.hpp +50 -0
  209. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma3_sse_register.hpp +50 -0
  210. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_fma4_register.hpp +50 -0
  211. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_i8mm_neon64_register.hpp +55 -0
  212. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon64_register.hpp +55 -0
  213. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_neon_register.hpp +130 -0
  214. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_register.hpp +96 -0
  215. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_rvv_register.hpp +491 -0
  216. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse2_register.hpp +59 -0
  217. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse3_register.hpp +49 -0
  218. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_1_register.hpp +48 -0
  219. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sse4_2_register.hpp +48 -0
  220. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_ssse3_register.hpp +48 -0
  221. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_sve_register.hpp +216 -0
  222. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_traits.hpp +409 -0
  223. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_utils.hpp +462 -0
  224. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vsx_register.hpp +84 -0
  225. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_vxe_register.hpp +86 -0
  226. package/third-party/common/phonemis/src/third-party/xsimd/types/xsimd_wasm_register.hpp +59 -0
  227. package/third-party/common/phonemis/src/third-party/xsimd/utils/bits.hpp +122 -0
  228. package/third-party/common/phonemis/src/third-party/xsimd/utils/xsimd_type_traits.hpp +145 -0
  229. package/third-party/common/phonemis/src/third-party/xsimd/xsimd.hpp +43 -0
  230. package/third-party/common/phonemis/src/phonemis/base/processor/constants.h +0 -11
  231. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/config.h +0 -16
  232. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.cpp +0 -271
  233. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/layer.h +0 -45
  234. package/third-party/common/phonemis/src/phonemis/base/processor/num2word/types.h +0 -29
  235. package/third-party/common/phonemis/src/phonemis/base/processor/sanitizer_layer.cpp +0 -22
  236. package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.cpp +0 -40
  237. package/third-party/common/phonemis/src/phonemis/base/processor/trim_layer.h +0 -22
@@ -0,0 +1,170 @@
1
+ #include "../../common/constants.h"
2
+ #include "../../common/text.h"
3
+ #include "layer.h"
4
+ #include <phonemis/utils/strings.h>
5
+ #include <phonemis/utils/unicode.h>
6
+
7
+ #include <vector>
8
+
9
+ namespace phonemis::processor::term2word {
10
+
11
+ using namespace utils;
12
+
13
+ namespace {
14
+ // Whether a host label is a top-level domain, which makes a bare host an address: example.com
15
+ bool is_domain(std::u32string_view label) {
16
+ static const std::unordered_set<std::u32string> domains = {U"com", U"org", U"net", U"edu", U"gov", U"io", U"pl",
17
+ U"de", U"fr", U"es", U"it", U"pt", U"uk", U"eu",
18
+ U"at", U"ch", U"info", U"tv", U"co", U"br"};
19
+ return domains.contains(strings::to_lower(label));
20
+ }
21
+
22
+ // A character of the user name of an email: jan.kowalski+news
23
+ bool is_user(char32_t c) {
24
+ return unicode::isword(c) || strings::contains(std::u32string_view(U".-+%"), c);
25
+ }
26
+
27
+ // A character of a host label: my-site
28
+ bool is_label(char32_t c) {
29
+ return unicode::isword(c) || c == U'-';
30
+ }
31
+
32
+ // A part of an address with its dots read: jan.kowalski -> jan dot kowalski
33
+ std::u32string dotted(std::u32string_view part, const std::u32string& dot) {
34
+ std::u32string words;
35
+ for (char32_t c : part) {
36
+ words += c == U'.' ? U" " + dot + U" " : std::u32string(1, c);
37
+ }
38
+ return words;
39
+ }
40
+ } // namespace
41
+
42
+ Term2WordLayer::Term2WordLayer(Terms terms) : terms_(std::move(terms)) {
43
+ for (const auto& [abbreviation, readings] : terms_.abbreviations) {
44
+ keys_.push_back(abbreviation);
45
+ }
46
+ }
47
+
48
+ void Term2WordLayer::apply(Document& document) const {
49
+ std::u32string_view text = document.text();
50
+
51
+ for (size_t position = 0; position < text.size();) {
52
+ // Terms start at word boundaries, but not after a dot: e.g. is not g.
53
+ std::optional<Term> term;
54
+ if (is_clear_before(text, position, U".")) {
55
+ term = read_address(text, position);
56
+ if (!term) {
57
+ term = read_abbreviation(text, position);
58
+ }
59
+ }
60
+
61
+ if (!term) {
62
+ position++;
63
+ continue;
64
+ }
65
+ document.replace(term->span, std::move(term->words));
66
+ position = term->span.end;
67
+ }
68
+ }
69
+
70
+ std::optional<Term2WordLayer::Term> Term2WordLayer::read_address(std::u32string_view text, size_t position) const {
71
+ const WebWords& web = terms_.web;
72
+
73
+ // 1. An optional scheme, which is not read: https://
74
+ size_t host = position;
75
+ while (host < text.size() && unicode::is_latin(text[host])) {
76
+ host++;
77
+ }
78
+ bool scheme = host > position && text.substr(host).starts_with(U"://");
79
+ host = scheme ? host + 3 : position;
80
+
81
+ // 2. The user of an email: jan.kowalski@
82
+ std::u32string_view user;
83
+ if (size_t at = host; !scheme) {
84
+ while (at < text.size() && is_user(text[at])) {
85
+ at++;
86
+ }
87
+ if (at > host && at < text.size() && text[at] == U'@') {
88
+ user = text.substr(host, at - host);
89
+ host = at + 1;
90
+ }
91
+ }
92
+
93
+ // 3. The host: labels joined by dots
94
+ std::vector<std::u32string_view> labels;
95
+ size_t end = host;
96
+ while (true) {
97
+ size_t label_end = end;
98
+ while (label_end < text.size() && is_label(text[label_end])) {
99
+ label_end++;
100
+ }
101
+ if (label_end == end) {
102
+ break;
103
+ }
104
+ labels.push_back(text.substr(end, label_end - end));
105
+ end = label_end;
106
+ if (end + 1 >= text.size() || text[end] != U'.' || !is_label(text[end + 1])) {
107
+ break;
108
+ }
109
+ end++;
110
+ }
111
+
112
+ // A bare host needs a known top-level domain: example.com, but not file.txt or e.g
113
+ bool www = !labels.empty() && strings::to_lower(labels[0]) == U"www";
114
+ bool domain = labels.size() >= 2 && is_domain(labels.back());
115
+ if (labels.size() < 2 || !(scheme || www || domain || !user.empty())) {
116
+ return std::nullopt;
117
+ }
118
+
119
+ // 4. An optional path, without the punctuation closing it: (see example.com/news).
120
+ size_t path_end = end;
121
+ if (end < text.size() && text[end] == U'/') {
122
+ while (path_end < text.size() && !unicode::isspace(text[path_end])) {
123
+ path_end++;
124
+ }
125
+ while (path_end > end + 1 && strings::contains(constants::kPunctuation, text[path_end - 1])) {
126
+ path_end--;
127
+ }
128
+ }
129
+
130
+ // 5. The words: jan at example dot com, w w w dot example dot com slash news
131
+ std::u32string words = user.empty() ? U"" : dotted(user, web.dot) + U" " + web.at + U" ";
132
+ for (size_t i = 0; i < labels.size(); ++i) {
133
+ bool spelled = i == 0 && www;
134
+ words += (i > 0 ? U" " + web.dot + U" " : U"") + (spelled ? U"w w w" : std::u32string(labels[i]));
135
+ }
136
+ for (auto path = text.substr(end, path_end - end); !path.empty();) {
137
+ size_t next = path.find(U'/', 1);
138
+ std::u32string_view segment = path.substr(1, next == std::u32string_view::npos ? next : next - 1);
139
+ words += U" " + web.slash + (segment.empty() ? U"" : U" " + dotted(segment, web.dot));
140
+ path = next == std::u32string_view::npos ? U"" : path.substr(next);
141
+ }
142
+ return Term{.span = {position, path_end}, .words = words};
143
+ }
144
+
145
+ std::optional<Term2WordLayer::Term> Term2WordLayer::read_abbreviation(std::u32string_view text, size_t position) const {
146
+ for (auto key : keys_at(text, position, keys_)) {
147
+ // An abbreviation ending with a letter ends a word, or touches a number: nr5
148
+ size_t end = position + key.size();
149
+ if (unicode::isword(key.back()) && !is_clear_after(text, end) && !unicode::isdigit(text[end])) {
150
+ continue;
151
+ }
152
+
153
+ auto words = read(terms_.abbreviations.at(std::u32string(key)), Context(text, {position, end}));
154
+ if (!words) {
155
+ continue;
156
+ }
157
+
158
+ // A dot which also ends the text stays with it; a word right after gets a space: nr5 -> numer 5
159
+ if (key.ends_with(U'.') && is_sentence_end(text, end)) {
160
+ *words += U".";
161
+ }
162
+ if (end < text.size() && unicode::isword(text[end])) {
163
+ *words += U" ";
164
+ }
165
+ return Term{.span = {position, end}, .words = *words};
166
+ }
167
+ return std::nullopt;
168
+ }
169
+
170
+ } // namespace phonemis::processor::term2word
@@ -0,0 +1,40 @@
1
+ #pragma once
2
+
3
+ #include "../../layer.h"
4
+ #include "types.h"
5
+
6
+ #include <optional>
7
+ #include <string>
8
+ #include <string_view>
9
+
10
+ namespace phonemis::processor::term2word {
11
+
12
+ /**
13
+ * Handles non-numeric and non-symbolic terms.
14
+ *
15
+ * Example: web addresses (www.example.com/news, jan@example.com), abbreviations (nr 5, e.g.).
16
+ */
17
+ class Term2WordLayer : public Layer {
18
+ public:
19
+ explicit Term2WordLayer(Terms terms);
20
+
21
+ void apply(Document& document) const override;
22
+
23
+ // A term found in a text, with its words
24
+ struct Term {
25
+ Span span;
26
+ std::u32string words;
27
+ };
28
+
29
+ private:
30
+ // https://www.example.com/news, jan.kowalski@example.pl: its parts joined with the words of the separators
31
+ std::optional<Term> read_address(std::u32string_view text, size_t position) const;
32
+
33
+ // The longest abbreviation at a position with a reading in its context
34
+ std::optional<Term> read_abbreviation(std::u32string_view text, size_t position) const;
35
+
36
+ Terms terms_;
37
+ Keys keys_; // Of the abbreviations
38
+ };
39
+
40
+ } // namespace phonemis::processor::term2word
@@ -0,0 +1,25 @@
1
+ #pragma once
2
+
3
+ #include "../../common/conditions.h"
4
+
5
+ #include <string>
6
+
7
+ namespace phonemis::processor::term2word {
8
+
9
+ // Abbreviations with their readings: nr -> numer before a number; St. -> Saint before a name, Street otherwise.
10
+ using Abbreviations = Readings;
11
+
12
+ // The words between the parts of a web address.
13
+ struct WebWords {
14
+ std::u32string dot; // example dot com
15
+ std::u32string at; // jan at example dot com
16
+ std::u32string slash; // example dot com slash news
17
+ };
18
+
19
+ // The terms of a language: written forms which are not said as written.
20
+ struct Terms {
21
+ Abbreviations abbreviations;
22
+ WebWords web;
23
+ };
24
+
25
+ } // namespace phonemis::processor::term2word
@@ -0,0 +1,47 @@
1
+ #include "layer.h"
2
+ #include <phonemis/utils/strings.h>
3
+ #include <phonemis/utils/unicode.h>
4
+
5
+ namespace phonemis::processor::trim {
6
+
7
+ namespace {
8
+ // Whether a position starts punctuation which takes no space before it: "a ," -> "a,", but "a ..." stays
9
+ bool is_closing_punctuation(std::u32string_view text, size_t position) {
10
+ if (position >= text.size()) {
11
+ return false;
12
+ }
13
+ bool ellipsis = text.substr(position).starts_with(U"...");
14
+ return utils::strings::contains(std::u32string_view(U",!?;:"), text[position]) ||
15
+ (text[position] == U'.' && !ellipsis);
16
+ }
17
+ } // namespace
18
+
19
+ void TrimLayer::apply(Document& document) const {
20
+ std::u32string_view text = document.text();
21
+
22
+ for (size_t i = 0; i < text.size();) {
23
+ if (!utils::unicode::isspace(text[i])) {
24
+ i++;
25
+ continue;
26
+ }
27
+
28
+ // Find the whole run of whitespaces
29
+ size_t end = i;
30
+ bool newline = false;
31
+ while (end < text.size() && utils::unicode::isspace(text[end])) {
32
+ newline |= text[end] == U'\n';
33
+ end++;
34
+ }
35
+
36
+ // Edges and spaces before punctuation lose the run entirely, the middle keeps a single character
37
+ bool closing = !newline && is_closing_punctuation(text, end);
38
+ std::u32string replacement = i == 0 || end == text.size() || closing ? U"" : newline ? U"\n" : U" ";
39
+ if (text.substr(i, end - i) != replacement) {
40
+ document.replace({i, end}, replacement);
41
+ }
42
+
43
+ i = end;
44
+ }
45
+ }
46
+
47
+ } // namespace phonemis::processor::trim
@@ -0,0 +1,19 @@
1
+ #pragma once
2
+
3
+ #include "../../layer.h"
4
+
5
+ namespace phonemis::processor::trim {
6
+
7
+ /**
8
+ * Normalizes whitespaces: removes them from both ends of the text and before punctuation (a , -> a,),
9
+ * and collapses every other run into a single "\n" (if it contains a line break) or " ".
10
+ */
11
+ class TrimLayer : public Layer {
12
+ public:
13
+ TrimLayer() = default;
14
+ ~TrimLayer() override = default;
15
+
16
+ void apply(Document& document) const override;
17
+ };
18
+
19
+ } // namespace phonemis::processor::trim
@@ -0,0 +1,60 @@
1
+ #include "layers/normalize/layer.h"
2
+ #include "layers/term2word/layer.h"
3
+ #include "layers/trim/layer.h"
4
+ #include "preprocessor.h"
5
+
6
+ namespace phonemis::processor {
7
+
8
+ namespace {
9
+ // A table with extra entries, which replace those with the same key
10
+ Readings merged(Readings table, const Readings& extra) {
11
+ for (const auto& [key, readings] : extra) {
12
+ table.insert_or_assign(key, readings);
13
+ }
14
+ return table;
15
+ }
16
+ } // namespace
17
+
18
+ Preprocessor make_preprocessor(const Config& config, const Language& language, std::unique_ptr<Layer> numbers) {
19
+ Preprocessor preprocessor;
20
+
21
+ // Characters normalized first, so that every layer sees one form of each
22
+ if (config.normalize) {
23
+ preprocessor.add_layer(std::make_unique<normalize::NormalizeLayer>(
24
+ language.characters ? *language.characters : normalize::constants::kCharacters));
25
+ }
26
+
27
+ if (config.trim) {
28
+ preprocessor.add_layer(std::make_unique<trim::TrimLayer>());
29
+ }
30
+
31
+ // Terms read before numbers, so that "nr 5" still sees its digit
32
+ if (config.terms && (language.terms || !config.abbreviations.empty())) {
33
+ term2word::Terms terms = language.terms ? *language.terms : term2word::Terms{};
34
+ terms.abbreviations = merged(std::move(terms.abbreviations), config.abbreviations);
35
+ preprocessor.add_layer(std::make_unique<term2word::Term2WordLayer>(std::move(terms)));
36
+ }
37
+
38
+ if (numbers) {
39
+ preprocessor.add_layer(std::move(numbers));
40
+ }
41
+
42
+ // Symbols read after numbers, which read the ones they own: 20%, $5
43
+ if (config.symbols && (language.symbols || !config.symbol_words.empty())) {
44
+ sym2word::Symbols symbols = language.symbols ? *language.symbols : sym2word::Symbols{};
45
+ preprocessor.add_layer(std::make_unique<sym2word::Sym2WordLayer>(merged(std::move(symbols), config.symbol_words)));
46
+ }
47
+
48
+ if (config.sanitize && language.replacements) {
49
+ const Replacements& replacements = *language.replacements;
50
+ auto mapper = [&replacements](char32_t c) {
51
+ auto it = replacements.find(c);
52
+ return it != replacements.end() ? it->second : c;
53
+ };
54
+ preprocessor.add_layer(std::make_unique<sanitizer::SanitizerLayer>([](char32_t) { return true; }, mapper));
55
+ }
56
+
57
+ return preprocessor;
58
+ }
59
+
60
+ } // namespace phonemis::processor
@@ -0,0 +1,45 @@
1
+ #pragma once
2
+
3
+ #include "config.h"
4
+ #include "layers/normalize/types.h"
5
+ #include "layers/sanitizer/layer.h"
6
+ #include "layers/sym2word/layer.h"
7
+ #include "layers/term2word/types.h"
8
+ #include "processor.h"
9
+
10
+ #include <concepts>
11
+ #include <memory>
12
+ #include <unordered_map>
13
+
14
+ namespace phonemis::processor {
15
+
16
+ // Characters replaced by others before phonemization.
17
+ using Replacements = std::unordered_map<char32_t, char32_t>;
18
+
19
+ // The parts of a language's preprocessing, from its constants.
20
+ struct Language {
21
+ const normalize::Characters* characters = nullptr; // Its normalized characters, if not the default ones
22
+ const term2word::Terms* terms = nullptr; // Its abbreviations and web words, if any
23
+ const sym2word::Symbols* symbols = nullptr; // Its symbol words, if any
24
+ const Replacements* replacements = nullptr; // Its character replacements, if any: ’ -> '
25
+ };
26
+
27
+ /**
28
+ * Builds a language's preprocessing from the layers its configuration enables, in their fixed order:
29
+ * Normalize, Trim, Term2Word, Num2Word, Sym2Word, Sanitizer.
30
+ * @param config which layers run, and words added to the language's tables.
31
+ * @param language the language's tables.
32
+ * @param numbers the language's Num2Word layer, or nullptr if numbers are not read.
33
+ */
34
+ Preprocessor make_preprocessor(const Config& config, const Language& language, std::unique_ptr<Layer> numbers);
35
+
36
+ /**
37
+ * Builds a language's preprocessing, as above.
38
+ * @tparam N2W the language's Num2Word layer, created only if numbers are read.
39
+ */
40
+ template <std::derived_from<Layer> N2W>
41
+ Preprocessor make_preprocessor(const Config& config, const Language& language) {
42
+ return make_preprocessor(config, language, config.numbers ? std::make_unique<N2W>() : nullptr);
43
+ }
44
+
45
+ } // namespace phonemis::processor
@@ -1,3 +1,4 @@
1
+ #include "common/document.h"
1
2
  #include "processor.h"
2
3
 
3
4
  namespace phonemis::processor {
@@ -7,17 +8,15 @@ void Processor::add_layer(std::unique_ptr<Layer> layer) {
7
8
  }
8
9
 
9
10
  std::u32string Processor::process(std::u32string_view input) const {
10
- if (layers_.empty()) {
11
- return std::u32string(input);
12
- }
13
-
14
- std::u32string result(input);
11
+ // We start by initializing a document - an object to work for all the layers.
12
+ Document document(input);
15
13
 
16
14
  for (const auto& layer : layers_) {
17
- result = layer->transform(result);
15
+ layer->apply(document);
16
+ document.commit();
18
17
  }
19
18
 
20
- return result;
19
+ return std::u32string(document.text());
21
20
  }
22
21
 
23
22
  } // namespace phonemis::processor
@@ -7,27 +7,31 @@
7
7
 
8
8
  namespace phonemis::processor {
9
9
 
10
- // Can be customized in derived classes by adding different preprocessing layers.
10
+ /**
11
+ * Main preprocessing component.
12
+ *
13
+ * A composition of elementary steps - can be customized by specyfing different layers (steps).
14
+ */
11
15
  class Processor {
12
16
  public:
13
17
  Processor() = default;
14
18
 
15
19
  /**
16
- * Allows to customize Processor's behavior by expanding it with custom layers.
17
- * @param layer a new transformation layer to be added.
18
- */
19
- void add_layer(std::unique_ptr<Layer> layer);
20
-
21
- /**
22
- * Processes the input by applying all of it's transformation layers.
20
+ * Processes the input by applying all of it's transformation layers, one after another.
21
+ * @details Everything the layers learn about the text lives in a Document, which is dropped on return.
23
22
  * @param input an input to be processed
24
23
  * @returns processed input.
25
24
  */
26
25
  std::u32string process(std::u32string_view input) const;
27
26
 
27
+ /**
28
+ * Allows to customize Processor's behavior by expanding it with custom layers.
29
+ * @param layer a new transformation layer to be added.
30
+ */
31
+ void add_layer(std::unique_ptr<Layer> layer);
32
+
28
33
  private:
29
- // Preprocessing layers - each layer performs some sort of
30
- // text -> text transformation on its input.
34
+ // Preprocessing layers - each layer queues edits of a shared document.
31
35
  std::vector<std::unique_ptr<Layer>> layers_;
32
36
  };
33
37
 
@@ -13,6 +13,11 @@ namespace phonemis::tagger {
13
13
 
14
14
  using namespace utils;
15
15
 
16
+ namespace {
17
+ // Small constant for unseen observations/transitions in log space.
18
+ const double LOG_EPSILON = std::log(1e-7);
19
+ } // namespace
20
+
16
21
  HMMTagger::HMMTagger(const Config& config) {
17
22
  if (!config.data_filepath.has_value()) {
18
23
  throw std::runtime_error("HMMTagger: data_filepath must be provided in the configuration.");
@@ -28,95 +33,127 @@ HMMTagger::HMMTagger(const Config& config) {
28
33
  }
29
34
  }
30
35
 
31
- // 1. Start Probabilities (defines the tag set)
32
- // Store as log probabilities (numeric stability)
36
+ // Parses a JSON probability into log-space, rejecting values outside [0, 1]
37
+ // (including negatives and NaN, which would make std::log produce NaN and
38
+ // silently corrupt the Viterbi computation). A stored 0 maps to -inf, which
39
+ // is valid in log space (an impossible event).
40
+ auto to_log_prob = [](const nlohmann::json& node, const std::string& context) -> double {
41
+ const double p = node.get<double>();
42
+ if (!(p >= 0.0 && p <= 1.0)) {
43
+ throw std::invalid_argument(
44
+ "HMMTagger: probability for '" + context + "' must be in [0, 1], got " + std::to_string(p));
45
+ }
46
+ return std::log(p);
47
+ };
48
+
49
+ // 1. The start-probability keys define the tag set. Assign each tag a dense
50
+ // integer id; sorting keeps the id assignment (and thus Viterbi
51
+ // tie-breaking) deterministic across runs.
52
+ for (const auto& [tag, prob] : json["start_prob"].items()) {
53
+ tag_names_.push_back(tag);
54
+ }
55
+ std::ranges::sort(tag_names_);
56
+
57
+ const size_t T = tag_names_.size();
58
+ std::unordered_map<Tag, size_t> tag_id;
59
+ tag_id.reserve(T);
60
+ for (size_t id = 0; id < T; ++id) {
61
+ tag_id.emplace(tag_names_[id], id);
62
+ }
63
+
64
+ // 2. Start probabilities, indexed by tag id (stored as log probabilities).
65
+ start_probs_.assign(T, LOG_EPSILON);
33
66
  for (const auto& [tag, prob] : json["start_prob"].items()) {
34
- tags_.insert(tag);
35
- start_probs_[tag] = std::log(prob.get<double>());
67
+ start_probs_[tag_id.at(tag)] = to_log_prob(prob, tag);
36
68
  }
37
69
 
38
- // 2. Emission Probabilities: P(Word | Tag)
39
- // Store as log probabilities (numeric stability)
70
+ // 3. Emission probabilities: P(Word | Tag), one word map per tag id. Tags
71
+ // not present in the start set (and therefore not in the model's state
72
+ // space) are ignored, mirroring the original behavior.
73
+ emission_probs_.resize(T);
40
74
  for (const auto& [tag, words] : json["emission"].items()) {
75
+ auto it = tag_id.find(tag);
76
+ if (it == tag_id.end()) continue;
41
77
  for (const auto& [word, prob] : words.items()) {
42
- emission_probs_[tag][word] = std::log(prob.get<double>());
78
+ emission_probs_[it->second][word] = to_log_prob(prob, tag + "/" + word);
43
79
  }
44
80
  }
45
81
 
46
- // 3. Transition Probabilities: P(Tag_i | Tag_{i-1})
47
- // Store as log probabilities (numeric stability)
82
+ // 4. Transition probabilities: P(Tag_i | Tag_{i-1}) as a flat T x T matrix,
83
+ // pre-filled with LOG_EPSILON so missing transitions need no runtime check.
84
+ transition_probs_.assign(T * T, LOG_EPSILON);
48
85
  for (const auto& [prev_tag, next_tags] : json["transition"].items()) {
86
+ auto prev_it = tag_id.find(prev_tag);
87
+ if (prev_it == tag_id.end()) continue;
49
88
  for (const auto& [next_tag, prob] : next_tags.items()) {
50
- transition_probs_[prev_tag][next_tag] = std::log(prob.get<double>());
89
+ auto next_it = tag_id.find(next_tag);
90
+ if (next_it == tag_id.end()) continue;
91
+ transition_probs_[prev_it->second * T + next_it->second] =
92
+ to_log_prob(prob, prev_tag + "->" + next_tag);
51
93
  }
52
94
  }
53
-
54
- // Ensure all tags exist in emission and transition maps
55
- for (const auto& tag : tags_) {
56
- emission_probs_[tag]; // Ensures outer map entry exists
57
- transition_probs_[tag]; // Ensures outer map entry exists
58
- }
59
95
  }
60
96
 
61
97
  void HMMTagger::tag_sentence(std::span<Token> sentence) const {
62
98
  if (sentence.empty()) return;
63
99
 
64
- // Small constant for unseen observations in log space
65
- const double LOG_EPSILON = std::log(1e-7);
100
+ const size_t T = tag_names_.size();
101
+ if (T == 0) return; // No model loaded -> nothing to tag.
102
+
103
+ const size_t n = sentence.size();
104
+
105
+ // The recursion only ever reads the previous column, so dp keeps just two
106
+ // rolling columns. backtrace keeps the full [step][state] grid of tag ids.
107
+ std::vector<double> dp_prev(T), dp_curr(T);
108
+ std::vector<std::vector<int>> backtrace(n, std::vector<int>(T, 0));
66
109
 
67
- // Viterbi tables: [step][state]
68
- std::vector<std::unordered_map<Tag, double>> dp(sentence.size());
69
- std::vector<std::unordered_map<Tag, Tag>> backtrace(sentence.size());
110
+ // log P(word | tag id), falling back to LOG_EPSILON for unseen words.
111
+ auto emit_log_prob = [this](size_t tag_id, const std::string& word) {
112
+ const auto& word_map = emission_probs_[tag_id];
113
+ auto it = word_map.find(word);
114
+ return it != word_map.end() ? it->second : LOG_EPSILON;
115
+ };
70
116
 
71
117
  // Step 0: Initialization
72
118
  std::string first_word = conversions::u32_to_utf8(
73
- should_lowercase(sentence[0].text) ?
74
- strings::to_lower(sentence[0].text) :
119
+ should_lowercase(sentence[0].text) ?
120
+ strings::to_lower(sentence[0].text) :
75
121
  sentence[0].text
76
122
  );
77
-
78
- for (const auto& tag : tags_) {
79
- double emit_p = emission_probs_.at(tag).contains(first_word)
80
- ? emission_probs_.at(tag).at(first_word) : LOG_EPSILON;
81
- dp[0][tag] = start_probs_.at(tag) + emit_p;
123
+ for (size_t tag = 0; tag < T; ++tag) {
124
+ dp_prev[tag] = start_probs_[tag] + emit_log_prob(tag, first_word);
82
125
  }
83
126
 
84
- // Steps 1..N: Viterbi Recursion
85
- for (size_t t = 1; t < sentence.size(); ++t) {
127
+ // Steps 1..n-1: Viterbi Recursion
128
+ for (size_t t = 1; t < n; ++t) {
86
129
  std::string word = conversions::u32_to_utf8(sentence[t].text);
87
130
 
88
- for (const auto& curr_tag : tags_) {
89
- double max_p = -std::numeric_limits<double>::infinity();
90
- Tag best_prev = *tags_.begin();
91
-
92
- double emit_p = emission_probs_.at(curr_tag).contains(word)
93
- ? emission_probs_.at(curr_tag).at(word) : LOG_EPSILON;
131
+ for (size_t curr = 0; curr < T; ++curr) {
132
+ const double emit_p = emit_log_prob(curr, word);
94
133
 
95
- for (const auto& prev_tag : tags_) {
96
- double trans_p = transition_probs_.at(prev_tag).contains(curr_tag)
97
- ? transition_probs_.at(prev_tag).at(curr_tag) : LOG_EPSILON;
98
-
99
- double p = dp[t - 1][prev_tag] + trans_p + emit_p;
134
+ double max_p = -std::numeric_limits<double>::infinity();
135
+ int best_prev = 0;
136
+ for (size_t prev = 0; prev < T; ++prev) {
137
+ const double p = dp_prev[prev] + transition_probs_[prev * T + curr] + emit_p;
100
138
  if (p > max_p) {
101
139
  max_p = p;
102
- best_prev = prev_tag;
140
+ best_prev = static_cast<int>(prev);
103
141
  }
104
142
  }
105
- dp[t][curr_tag] = max_p;
106
- backtrace[t][curr_tag] = best_prev;
143
+ dp_curr[curr] = max_p;
144
+ backtrace[t][curr] = best_prev;
107
145
  }
146
+ std::swap(dp_prev, dp_curr); // dp_prev now holds the column for step t.
108
147
  }
109
148
 
110
- // Backtracking: Find most likely end state and reverse-walk the path
111
- auto last_idx = sentence.size() - 1;
112
- Tag current_tag = *std::max_element(tags_.begin(), tags_.end(), [&](const Tag& a, const Tag& b) {
113
- return dp[last_idx][a] < dp[last_idx][b];
114
- });
149
+ // Backtracking: pick the most likely final state (dp_prev holds the last
150
+ // column after the final swap), then reverse-walk the path.
151
+ int current = static_cast<int>(std::ranges::max_element(dp_prev) - dp_prev.begin());
115
152
 
116
- for (size_t t = last_idx; ; --t) {
117
- sentence[t].tag = current_tag;
153
+ for (size_t t = n - 1; ; --t) {
154
+ sentence[t].tag = tag_names_[current];
118
155
  if (t == 0) break;
119
- current_tag = backtrace[t][current_tag];
156
+ current = backtrace[t][current];
120
157
  }
121
158
  }
122
159